diff --git a/.buildkite/pipelines/artifacts.yml b/.buildkite/pipelines/artifacts.yml index 8e08c736694e8..b6d3cc9fc9b14 100644 --- a/.buildkite/pipelines/artifacts.yml +++ b/.buildkite/pipelines/artifacts.yml @@ -73,13 +73,23 @@ steps: - command: .buildkite/scripts/steps/artifacts/cloud.sh label: 'Cloud Deployment' - soft_fail: true + soft_fail: + - exit_status: 255 agents: queue: n2-2 timeout_in_minutes: 30 if: "build.env('RELEASE_BUILD') == null || build.env('RELEASE_BUILD') == '' || build.env('RELEASE_BUILD') == 'false'" retry: automatic: + # Timeout and graceful shutdown | ecctl deployment create falure + - exit_status: 255 + limit: 0 + + # Timeout and forced shutdown + - exit_status: '-1' + limit: 0 + + # Test failures - exit_status: '*' limit: 1 diff --git a/.buildkite/scripts/steps/artifacts/cloud.sh b/.buildkite/scripts/steps/artifacts/cloud.sh index 5bf2285ab162a..4d2317ce0b6c7 100644 --- a/.buildkite/scripts/steps/artifacts/cloud.sh +++ b/.buildkite/scripts/steps/artifacts/cloud.sh @@ -12,28 +12,28 @@ mkdir -p target download_artifact "kibana-$FULL_VERSION-linux-x86_64.tar.gz" ./target --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" -node scripts/build \ - --skip-initialize \ - --skip-generic-folders \ - --skip-platform-folders \ - --skip-archives \ - --docker-images \ - --skip-docker-ubi \ - --skip-docker-ubuntu \ - --skip-docker-contexts - -docker load --input target/kibana-cloud-$FULL_VERSION-docker-image.tar.gz - TAG="$FULL_VERSION-$GIT_COMMIT" -KIBANA_BASE_IMAGE="docker.elastic.co/kibana-ci/kibana-cloud:$FULL_VERSION" KIBANA_TEST_IMAGE="docker.elastic.co/kibana-ci/kibana-cloud:$TAG" -docker tag "$KIBANA_BASE_IMAGE" "$KIBANA_TEST_IMAGE" - echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co trap 'docker logout docker.elastic.co' EXIT -docker push "$KIBANA_TEST_IMAGE" +if docker manifest inspect $KIBANA_TEST_IMAGE &> /dev/null; then + echo "Distribution already exists, skipping build" +else + node scripts/build \ + --skip-initialize \ + --skip-generic-folders \ + --skip-platform-folders \ + --skip-archives \ + --docker-images \ + --docker-tag-qualifier="$GIT_COMMIT" \ + --docker-push \ + --skip-docker-ubi \ + --skip-docker-ubuntu \ + --skip-docker-contexts +fi + docker logout docker.elastic.co echo "--- Create deployment" @@ -62,6 +62,7 @@ function shutdown { trap "shutdown" EXIT ecctl deployment create --track --output json --file "$DEPLOYMENT_SPEC" > "$LOGS" + CLOUD_DEPLOYMENT_USERNAME=$(jq -r --slurp '.[]|select(.resources).resources[] | select(.credentials).credentials.username' "$LOGS") CLOUD_DEPLOYMENT_PASSWORD=$(jq -r --slurp '.[]|select(.resources).resources[] | select(.credentials).credentials.password' "$LOGS") CLOUD_DEPLOYMENT_ID=$(jq -r --slurp '.[0].id' "$LOGS") diff --git a/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh index 490d2b4a3b705..a8711c8d2f58a 100755 --- a/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh +++ b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh @@ -22,7 +22,7 @@ for i in "${scalabilityJourneys[@]}"; do echo "Looking for JOURNEY=${JOURNEY_NAME} and BUILD_ID=${BUILD_ID} in APM traces" node scripts/extract_performance_testing_dataset \ - --config "x-pack/test/performance/journeys/${i}/config.ts" \ \ + --config "x-pack/test/performance/journeys/${i}/config.ts" \ --buildId "${BUILD_ID}" \ --es-url "${ES_SERVER_URL}" \ --es-username "${USER_FROM_VAULT}" \ diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 0e3c26c64fa6b..56520b0ab6d03 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: 2022-09-06 +date: 2022-09-07 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 44f6e2ebbb5c3..7ce9f03610600 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: 2022-09-06 +date: 2022-09-07 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 082d6130630c3..86745b348b398 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index a71cca2e3d278..851021c210af7 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -1827,7 +1827,7 @@ "section": "def-server.RuleExecutorServices", "text": "RuleExecutorServices" }, - "" + "" ], "path": "x-pack/plugins/alerting/server/types.ts", "deprecated": false, @@ -1899,22 +1899,14 @@ { "parentPluginId": "alerting", "id": "def-server.RuleExecutorServices.alertFactory", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "alertFactory", "description": [], "signature": [ - "{ create: (id: string) => ", - { - "pluginId": "alerting", - "scope": "server", - "docId": "kibAlertingPluginApi", - "section": "def-server.PublicAlert", - "text": "PublicAlert" - }, - "; hasReachedAlertLimit: () => boolean; done: () => ", - "AlertFactoryDoneUtils", - "; }" + "Pick<", + "AlertFactory", + ", \"done\" | \"create\"> & { alertLimit: Pick<{ getValue: () => number; setLimitReached: (reached: boolean) => void; checkLimitUsage: () => void; }, \"getValue\" | \"setLimitReached\">; }" ], "path": "x-pack/plugins/alerting/server/types.ts", "deprecated": false diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index b6e7a122b99d8..8a9bf5a7bf6d3 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: 2022-09-06 +date: 2022-09-07 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 ed625467a5aa2..8978452761490 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -766,7 +766,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}\" | \"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/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/{serviceName}/serviceNodes\" | \"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\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"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}/profiling/timeline\" | \"GET /internal/apm/services/{serviceName}/profiling/statistics\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/sorted_and_filtered_services\" | \"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/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\" | \"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/alerts/chart_preview/transaction_error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_duration\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"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\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"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\" | \"POST /internal/apm/correlations/field_stats/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\" | \"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\"" + "\"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}\" | \"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/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/{serviceName}/serviceNodes\" | \"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\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"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}/profiling/timeline\" | \"GET /internal/apm/services/{serviceName}/profiling/statistics\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/sorted_and_filtered_services\" | \"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/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\" | \"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/alerts/chart_preview/transaction_error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_duration\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"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\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"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\" | \"POST /internal/apm/correlations/field_stats/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\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"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\"" ], "path": "x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -1034,6 +1034,180 @@ "SpanLinkDetails", "[]; }, ", "APMRouteCreateOptions", + ">; \"GET /internal/apm/services/{serviceName}/storage_details\": ", + "ServerRoute", + "<\"GET /internal/apm/services/{serviceName}/storage_details\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ serviceName: ", + "StringC", + "; }>; query: ", + "IntersectionC", + "<[", + "TypeC", + "<{ indexLifecyclePhase: ", + "UnionC", + "<[", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".All>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Hot>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Warm>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Cold>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Frozen>]>; }>, ", + "TypeC", + "<{ probability: ", + "Type", + "; }>, ", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"ENVIRONMENT_NOT_DEFINED\">, ", + "LiteralC", + "<\"ENVIRONMENT_ALL\">, ", + "BrandC", + "<", + "StringC", + ", ", + "NonEmptyStringBrand", + ">]>; }>, ", + "TypeC", + "<{ kuery: ", + "StringC", + "; }>, ", + "TypeC", + "<{ start: ", + "Type", + "; end: ", + "Type", + "; }>]>; }>, ", + { + "pluginId": "apm", + "scope": "server", + "docId": "kibApmPluginApi", + "section": "def-server.APMRouteHandlerResources", + "text": "APMRouteHandlerResources" + }, + ", { processorEventStats: { processorEvent: ", + { + "pluginId": "observability", + "scope": "common", + "docId": "kibObservabilityPluginApi", + "section": "def-common.ProcessorEvent", + "text": "ProcessorEvent" + }, + ".transaction | ", + { + "pluginId": "observability", + "scope": "common", + "docId": "kibObservabilityPluginApi", + "section": "def-common.ProcessorEvent", + "text": "ProcessorEvent" + }, + ".error | ", + { + "pluginId": "observability", + "scope": "common", + "docId": "kibObservabilityPluginApi", + "section": "def-common.ProcessorEvent", + "text": "ProcessorEvent" + }, + ".metric | ", + { + "pluginId": "observability", + "scope": "common", + "docId": "kibObservabilityPluginApi", + "section": "def-common.ProcessorEvent", + "text": "ProcessorEvent" + }, + ".span; docs: number; size: number; }[]; }, ", + "APMRouteCreateOptions", + ">; \"GET /internal/apm/storage_explorer\": ", + "ServerRoute", + "<\"GET /internal/apm/storage_explorer\", ", + "TypeC", + "<{ query: ", + "IntersectionC", + "<[", + "TypeC", + "<{ indexLifecyclePhase: ", + "UnionC", + "<[", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".All>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Hot>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Warm>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Cold>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Frozen>]>; }>, ", + "TypeC", + "<{ probability: ", + "Type", + "; }>, ", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"ENVIRONMENT_NOT_DEFINED\">, ", + "LiteralC", + "<\"ENVIRONMENT_ALL\">, ", + "BrandC", + "<", + "StringC", + ", ", + "NonEmptyStringBrand", + ">]>; }>, ", + "TypeC", + "<{ kuery: ", + "StringC", + "; }>, ", + "TypeC", + "<{ start: ", + "Type", + "; end: ", + "Type", + "; }>]>; }>, ", + { + "pluginId": "apm", + "scope": "server", + "docId": "kibApmPluginApi", + "section": "def-server.APMRouteHandlerResources", + "text": "APMRouteHandlerResources" + }, + ", { serviceStatistics: { serviceName: string; environments: string[]; size?: number | undefined; agentName: ", + "AgentName", + "; sampling: number; }[]; }, ", + "APMRouteCreateOptions", ">; \"POST /api/apm/agent_keys\": ", "ServerRoute", "<\"POST /api/apm/agent_keys\", ", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 4e114e6ce7d9d..2e85e0fe7e82f 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; @@ -21,7 +21,7 @@ Contact [APM UI](https://github.com/orgs/elastic/teams/apm-ui) for questions reg | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 39 | 0 | 39 | 53 | +| 39 | 0 | 39 | 54 | ## Client diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index b35e46e031865..c190b176152f5 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: 2022-09-06 +date: 2022-09-07 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 0f57ba70b9999..b0c8194872e26 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: 2022-09-06 +date: 2022-09-07 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 1089cd897c6b7..276cbb7914051 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: 2022-09-06 +date: 2022-09-07 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 bae623080dd49..de34d7c6f182f 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: 2022-09-06 +date: 2022-09-07 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 119f98090c69c..b5f95655fc358 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: 2022-09-06 +date: 2022-09-07 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 5b3e4e0cbf942..aebf49b716dd5 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 3e8905668c8d4..0276b3739e55a 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: 2022-09-06 +date: 2022-09-07 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 9c4fd3cc41fa2..7a23bea6db7cd 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 20457823815f1..c21c9db5e5463 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.mdx b/api_docs/core.mdx index b6ad920381910..edf01ee9974ec 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index fdf8aa7725835..625584f45193f 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: 2022-09-06 +date: 2022-09-07 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 65c57db79c2af..2cd9fcacf7d98 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: 2022-09-06 +date: 2022-09-07 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 aa2aa87ee52a7..b077c478de0cc 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 99552239dbef9..f54e2978ca8a6 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: 2022-09-06 +date: 2022-09-07 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 d6720e43f79a0..18b53b499a333 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index f14af11637017..74e13d9570f5b 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: 2022-09-06 +date: 2022-09-07 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 fc634e4d510b6..7ca46f2f9b2f6 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: 2022-09-06 +date: 2022-09-07 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 c6c3f2195b0d4..0cdf89833b5ed 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: 2022-09-06 +date: 2022-09-07 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 d1bd53a5b473d..1d2347c970684 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 36572e12aa0fb..3d988fdd09ce8 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: 2022-09-06 +date: 2022-09-07 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 e4d5440bfdc62..4e9386cdeb115 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: 2022-09-06 +date: 2022-09-07 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 2787cf4d1e205..fae055410e80d 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 97608bb1d2beb..6ee2ce05ca346 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 52bc8fb97533a..a1ed349b543ae 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 0873f90539744..b64cb0dc30471 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: 2022-09-06 +date: 2022-09-07 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 f3620385ddcae..e2b70f2f8e129 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: 2022-09-06 +date: 2022-09-07 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 11b53e7d81f85..dc4decbf959c0 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 9e302d3ec385f..1374109c4a066 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: 2022-09-06 +date: 2022-09-07 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 f83fd634dea26..2af55b586ee99 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: 2022-09-06 +date: 2022-09-07 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 ff50fc4962b25..07f95dd49ff0c 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: 2022-09-06 +date: 2022-09-07 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 2f4a97083f119..afde3b98dcafe 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: 2022-09-06 +date: 2022-09-07 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 aea9650756ddc..cb4c439d84790 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: 2022-09-06 +date: 2022-09-07 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 2b74a26ae25c4..b3c097214b2d3 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: 2022-09-06 +date: 2022-09-07 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 6b34c4088e9ae..3a80cc15aa5cf 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index ff4979afd3ce7..6e02317db49a7 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: 2022-09-06 +date: 2022-09-07 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 750366930004f..594f3bb4d24d1 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: 2022-09-06 +date: 2022-09-07 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 a58b72f20bf77..3e69a97bd0c00 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: 2022-09-06 +date: 2022-09-07 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 54fcdd0bde2a4..73cc90d657426 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: 2022-09-06 +date: 2022-09-07 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 7964d4d21c4c3..e4093ceac07ae 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: 2022-09-06 +date: 2022-09-07 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 cece7f9474b8b..be5929fd38d56 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: 2022-09-06 +date: 2022-09-07 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 fbfa28ccf68c7..4a0503e48459f 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: 2022-09-06 +date: 2022-09-07 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 d063673a50c14..953697f936c6b 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: 2022-09-06 +date: 2022-09-07 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 2b6681ae06216..e540c032935db 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: 2022-09-06 +date: 2022-09-07 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 c93ab7f345bc4..e7342d04da9da 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: 2022-09-06 +date: 2022-09-07 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 ac98185499d10..fbd0b000fa930 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: 2022-09-06 +date: 2022-09-07 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 daaad8d675c9c..a0056c7338d74 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: 2022-09-06 +date: 2022-09-07 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 5ebc4a9811f32..a395c0c178b5c 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: 2022-09-06 +date: 2022-09-07 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 e10f5c5cbe402..fdc07fa4327a9 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: 2022-09-06 +date: 2022-09-07 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 1776e4a6585bf..20d3e481faa4c 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: 2022-09-06 +date: 2022-09-07 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 d5aed3858f71a..34912547e5471 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: 2022-09-06 +date: 2022-09-07 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 c283935c62f52..9f538899acbab 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: 2022-09-06 +date: 2022-09-07 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 46d4068ca249f..ecbf04950569d 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index cfd98cbe95dc6..ee8c31d150902 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: 2022-09-06 +date: 2022-09-07 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 ab12dca075c19..64fc79df18c3a 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 55e90c0516af2..d60024bdaed5b 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index de87d26f24bf2..e506c2a95a464 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 3c324136f039e..d1ee511f1b26d 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 939d4b806fb0b..73daf81d33975 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: 2022-09-06 +date: 2022-09-07 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 3acad0bdb0c5b..f6aab686bb8df 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: 2022-09-06 +date: 2022-09-07 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 2ada44f7c245e..5b566585577e8 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: 2022-09-06 +date: 2022-09-07 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 dce075233c681..c01b9a72036c9 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: 2022-09-06 +date: 2022-09-07 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 8fe2e18f681e4..632f2a5f148c2 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: 2022-09-06 +date: 2022-09-07 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 35adae134ef06..f3c241c01bfe0 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 6be99347740b7..2080343d9bd79 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 26b13591e0df1..e6b1e884f8289 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: 2022-09-06 +date: 2022-09-07 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 eb9dd53dad393..a09dd84d4427a 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: 2022-09-06 +date: 2022-09-07 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 cf097f3adea04..51a1b4b37dfa7 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: 2022-09-06 +date: 2022-09-07 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 f5a13ef20025c..2e323e811b1e6 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: 2022-09-06 +date: 2022-09-07 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 0b4ace5c97070..bfe5e552baf73 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: 2022-09-06 +date: 2022-09-07 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 2a43b4ad73fd1..9c204be3d61a9 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 41d191bffce76..3456e4a1954b6 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: 2022-09-06 +date: 2022-09-07 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.devdocs.json b/api_docs/kbn_apm_synthtrace.devdocs.json index 142d988330fbd..02fecd0c5eb47 100644 --- a/api_docs/kbn_apm_synthtrace.devdocs.json +++ b/api_docs/kbn_apm_synthtrace.devdocs.json @@ -192,13 +192,28 @@ "label": "refresh", "description": [], "signature": [ - "() => Promise<", + "(dataStreams?: string[] | undefined) => Promise<", "ShardsOperationResponseBase", ">" ], "path": "packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", "deprecated": false, - "children": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace", + "id": "def-server.ApmSynthtraceEsClient.refresh.$1", + "type": "Array", + "tags": [], + "label": "dataStreams", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", + "deprecated": false, + "isRequired": false + } + ], "returnComment": [] }, { @@ -455,10 +470,10 @@ }, { "parentPluginId": "@kbn/apm-synthtrace", - "id": "def-server.EntityArrayIterable.ratePerMinute", + "id": "def-server.EntityArrayIterable.estimatedRatePerMinute", "type": "Function", "tags": [], - "label": "ratePerMinute", + "label": "estimatedRatePerMinute", "description": [], "signature": [ "() => number" @@ -805,10 +820,10 @@ }, { "parentPluginId": "@kbn/apm-synthtrace", - "id": "def-server.EntityIterable.ratePerMinute", + "id": "def-server.EntityIterable.estimatedRatePerMinute", "type": "Function", "tags": [], - "label": "ratePerMinute", + "label": "estimatedRatePerMinute", "description": [], "signature": [ "() => number" diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 4f40b0e9ba98b..9aa1c9da7a26c 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 75 | 0 | 75 | 12 | +| 76 | 0 | 76 | 12 | ## Server diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 227a95bbdc22e..80300e4b34789 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: 2022-09-06 +date: 2022-09-07 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 b2ffd9c43784b..de6bb8999931b 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 858efed44444c..263e793b011a0 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: 2022-09-06 +date: 2022-09-07 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 402bf52a1f22f..d65af3ce467da 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: 2022-09-06 +date: 2022-09-07 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 5968959cd3961..7e24e3e42435a 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: 2022-09-06 +date: 2022-09-07 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 97e4463e24e0c..64816e9963b89 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: 2022-09-06 +date: 2022-09-07 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 5f71bc4cde520..6ea459146251a 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 6a345a79bc6c4..5c72a688d0b17 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: 2022-09-06 +date: 2022-09-07 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 293f300c3b260..abea714b90bf9 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: 2022-09-06 +date: 2022-09-07 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 f763976f6427b..49893743e1d9d 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: 2022-09-06 +date: 2022-09-07 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 1047ba87b6643..a651937e567c1 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 77444ae7f6a05..cced6551c1ff8 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: 2022-09-06 +date: 2022-09-07 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 5ac4b3104f341..e67c33689204f 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: 2022-09-06 +date: 2022-09-07 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 01e1c13a9fd76..a8122ae1841df 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: 2022-09-06 +date: 2022-09-07 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 514ed035def29..a4c7c89224b47 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: 2022-09-06 +date: 2022-09-07 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 4d02dee208b91..71b38bfeeb32a 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: 2022-09-06 +date: 2022-09-07 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 b68bbadb1b949..ff3b13f7e0bab 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: 2022-09-06 +date: 2022-09-07 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 9e0a126f80542..f2a9a027e387c 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: 2022-09-06 +date: 2022-09-07 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 ebb267f8b9dc8..3a170052d9bd0 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: 2022-09-06 +date: 2022-09-07 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 aa6cf87a21193..49e6be4e8bcf5 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: 2022-09-06 +date: 2022-09-07 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 b7a0df2ff0356..506c874ec4cd2 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: 2022-09-06 +date: 2022-09-07 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 2cf240d31d76f..8491ba8253b9e 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: 2022-09-06 +date: 2022-09-07 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 82457300e0524..5a7de06ce3e5b 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: 2022-09-06 +date: 2022-09-07 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 5c75439893d98..e52771051b34c 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: 2022-09-06 +date: 2022-09-07 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 72020ac2bcd34..975a976dc152b 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: 2022-09-06 +date: 2022-09-07 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 afe35769d50b1..a12796b3af15c 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: 2022-09-06 +date: 2022-09-07 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 762be5fb47ecc..1608bdfdf6db8 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: 2022-09-06 +date: 2022-09-07 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 1c58618bae110..b8e203778c69d 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: 2022-09-06 +date: 2022-09-07 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 eea481cff9efc..ac75a699b4b89 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: 2022-09-06 +date: 2022-09-07 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 3fa5ef98dce64..4fee44df05051 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: 2022-09-06 +date: 2022-09-07 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 923481e4ccd64..3f1d420fb18ec 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: 2022-09-06 +date: 2022-09-07 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 2c2bf980a2122..d911455b3497b 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: 2022-09-06 +date: 2022-09-07 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 63c057fa342a6..060e08b9305d1 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: 2022-09-06 +date: 2022-09-07 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 b1d80d415129e..afea5944f3b5f 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: 2022-09-06 +date: 2022-09-07 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 2d18319a276b3..9366812463eb7 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: 2022-09-06 +date: 2022-09-07 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 b4deab8346f2f..6419e8fc767cf 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: 2022-09-06 +date: 2022-09-07 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 06dec051aa6f7..3b4b05add69c6 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: 2022-09-06 +date: 2022-09-07 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 56a00817b13a9..02f5d9aaaad9a 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: 2022-09-06 +date: 2022-09-07 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 60aecf7424070..6756e8014d30b 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: 2022-09-06 +date: 2022-09-07 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 ee9d22702424b..6ba9bf598cc7e 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: 2022-09-06 +date: 2022-09-07 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 a07efab80f14b..11e7d4685bcb1 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: 2022-09-06 +date: 2022-09-07 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 a8989f6957230..d06cec3abe119 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: 2022-09-06 +date: 2022-09-07 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 2b0274399ae1f..00abb6d88e2e1 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: 2022-09-06 +date: 2022-09-07 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 9fa17a347e6e0..ad85c66cb2b3b 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: 2022-09-06 +date: 2022-09-07 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 23dc5d40c40ce..9042a25c2da51 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index e51873a4ffe4c..34dfef3b2d414 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index a2947b7d28466..e3dd1661d7e13 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 48c485334349e..29efd2e709aea 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 89ebe82053604..3595e65887876 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: 2022-09-06 +date: 2022-09-07 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 dad8a14043fae..d0a30864e23aa 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: 2022-09-06 +date: 2022-09-07 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 8724bf34779ad..e45a81606c8c7 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: 2022-09-06 +date: 2022-09-07 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 5008328a2b6ef..e4b3b1e95cd2a 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: 2022-09-06 +date: 2022-09-07 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 e5a186c7fafc1..3ab2769e33a69 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: 2022-09-06 +date: 2022-09-07 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 602b4baf794fa..4db2dafa511d5 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: 2022-09-06 +date: 2022-09-07 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 cef1df0e4ed92..fb3e552f693e1 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: 2022-09-06 +date: 2022-09-07 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 38f8fc08be9f5..5f87409e412fa 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: 2022-09-06 +date: 2022-09-07 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 36f42437d1100..9afcc9d17b590 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: 2022-09-06 +date: 2022-09-07 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 2351e1c161fe1..0700a5bd07585 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: 2022-09-06 +date: 2022-09-07 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 058eb732b1480..e1e3ac247fa36 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: 2022-09-06 +date: 2022-09-07 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 a0976872ed52b..72996ffb91fc1 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: 2022-09-06 +date: 2022-09-07 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 2850d141de4e2..8cc109d0568c4 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: 2022-09-06 +date: 2022-09-07 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 1f22e8afefdd5..ede088fc5c6ed 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: 2022-09-06 +date: 2022-09-07 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 072f8e7467992..0710d493a91de 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: 2022-09-06 +date: 2022-09-07 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 cd815fc819739..549356ea922dd 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: 2022-09-06 +date: 2022-09-07 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_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 97203df57f813..743c5faad92dd 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: 2022-09-06 +date: 2022-09-07 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 57b23bc813c76..3765c360063b5 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_core_http_server.mdx index 5ea85d8edec6c..cc2d7b611c770 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: 2022-09-06 +date: 2022-09-07 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 67fbb1f0bc13f..563876c4a598f 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: 2022-09-06 +date: 2022-09-07 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 a8f08144c9c6f..bc9ac7b997e58 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: 2022-09-06 +date: 2022-09-07 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 118c6d56caa16..d94defae7a668 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: 2022-09-06 +date: 2022-09-07 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 2eebb6ad333a7..3e96a8b24521f 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: 2022-09-06 +date: 2022-09-07 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 6767d040b15bd..c203ca6978159 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: 2022-09-06 +date: 2022-09-07 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 e2a3f1ccdba2a..d5ddcabbc6999 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: 2022-09-06 +date: 2022-09-07 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 0f63e78634e39..23ac231f7db12 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 8cbbf6ec24436..cec8c3d113cd1 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 43feff34842f8..1b9cd30610cca 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: 2022-09-06 +date: 2022-09-07 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 c714e9b9aff2e..5b9fc23e7d3a8 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: 2022-09-06 +date: 2022-09-07 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 e16dedb121036..4a81fe638eeab 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: 2022-09-06 +date: 2022-09-07 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index e5f404928d37a..05112105e62f3 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: 2022-09-06 +date: 2022-09-07 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 f183ecbb1dcdd..11957d79313cd 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: 2022-09-06 +date: 2022-09-07 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 c3cb65884623f..c18e67ef31e11 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: 2022-09-06 +date: 2022-09-07 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 00bc0e02d96ed..d6fe2392825a7 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: 2022-09-06 +date: 2022-09-07 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 1fe22f5e361a1..9744bead247c6 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: 2022-09-06 +date: 2022-09-07 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 e98d848279e20..12ac7ef4a9186 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: 2022-09-06 +date: 2022-09-07 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 c3a18f9c6a33e..97d5b806d4c9c 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: 2022-09-06 +date: 2022-09-07 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 677c0a7dded87..ad77881dbde21 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: 2022-09-06 +date: 2022-09-07 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 0d3b2d3504b7a..796cbbf70a804 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: 2022-09-06 +date: 2022-09-07 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 1ff5ddeb716ed..beae867b6bf46 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: 2022-09-06 +date: 2022-09-07 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 aae4a1dc3b3d2..affb582e10268 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: 2022-09-06 +date: 2022-09-07 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 337b689956aa4..4dace8ae3cb17 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: 2022-09-06 +date: 2022-09-07 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 e6803d2caa7b1..c1c3edd3c57e4 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: 2022-09-06 +date: 2022-09-07 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 232561f049ce9..3db1fb6f2dd25 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: 2022-09-06 +date: 2022-09-07 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 97ee4827d1b68..885fd16e2b57d 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: 2022-09-06 +date: 2022-09-07 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 0bea9b5c52b6b..2889cd8452e04 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: 2022-09-06 +date: 2022-09-07 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 1a9a5dbede6a8..2c1b459fca665 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: 2022-09-06 +date: 2022-09-07 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 99284e993eab5..933e7d688b1c8 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: 2022-09-06 +date: 2022-09-07 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index d5a2cd88b46d3..547fe04c217b3 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: 2022-09-06 +date: 2022-09-07 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 e1f23bc02e5d9..1ccfed0911f4b 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: 2022-09-06 +date: 2022-09-07 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.devdocs.json b/api_docs/kbn_core_rendering_browser_mocks.devdocs.json new file mode 100644 index 0000000000000..f405033465ab1 --- /dev/null +++ b/api_docs/kbn_core_rendering_browser_mocks.devdocs.json @@ -0,0 +1,56 @@ +{ + "id": "@kbn/core-rendering-browser-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "@kbn/core-rendering-browser-mocks", + "id": "def-common.renderingServiceMock", + "type": "Object", + "tags": [], + "label": "renderingServiceMock", + "description": [], + "path": "packages/core/rendering/core-rendering-browser-mocks/src/rendering_service.mock.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-rendering-browser-mocks", + "id": "def-common.renderingServiceMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => jest.Mocked" + ], + "path": "packages/core/rendering/core-rendering-browser-mocks/src/rendering_service.mock.ts", + "deprecated": false, + "returnComment": [], + "children": [] + } + ], + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx new file mode 100644 index 0000000000000..77e0b0a1680d8 --- /dev/null +++ b/api_docs/kbn_core_rendering_browser_mocks.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: kibKbnCoreRenderingBrowserMocksPluginApi +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: 2022-09-07 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] +--- +import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 2 | 0 | + +## Common + +### Objects + + diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 36fadbfa56b7c..53a2455a429b5 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 9d78c2e380058..fd5b8c0a19644 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index dfef7401613af..3cef779cc7260 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; 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 bebb651b194ef..f895de6bfb934 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: 2022-09-06 +date: 2022-09-07 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 376b28746f40c..11394ad658932 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: 2022-09-06 +date: 2022-09-07 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 2ecce4465af82..c9dfd0f25e3e0 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: 2022-09-06 +date: 2022-09-07 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 00f720e9d5185..3356adad0bd61 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: 2022-09-06 +date: 2022-09-07 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 b3c9ce41ddea5..d232918c826a2 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: 2022-09-06 +date: 2022-09-07 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 1da0166565b44..4cc117386d965 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: 2022-09-06 +date: 2022-09-07 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 bcc2e28dcd51a..f58f02d51a311 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: 2022-09-06 +date: 2022-09-07 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 6b7ee7af63ae4..e27ea4e4d13ee 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: 2022-09-06 +date: 2022-09-07 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 af070bd29e18f..2eabd993dc56a 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index fb6abb141aa95..53a8f144297bd 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; 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 7da28fe940c34..9e1eb09b68d6e 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: 2022-09-06 +date: 2022-09-07 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 db9eb21dea863..001f3310eb837 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: 2022-09-06 +date: 2022-09-07 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 ddb5b3dfa0156..4f71783ecfb78 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: 2022-09-06 +date: 2022-09-07 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 4d967c6814712..8dbfbc37b91a1 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: 2022-09-06 +date: 2022-09-07 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 8b918ee989f7e..8445223e4eb69 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: 2022-09-06 +date: 2022-09-07 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_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index cc647fd4f441b..5f832eb197d7e 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: 2022-09-06 +date: 2022-09-07 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 eb661078b5acc..9cd629f974d1c 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: 2022-09-06 +date: 2022-09-07 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index bcfd796c5a5ca..be476afa5a8fe 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: 2022-09-06 +date: 2022-09-07 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_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 2f70442ddfba1..44f2d7de967ff 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 86a7fd3fd90a5..0fa3aa1e736e6 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: 2022-09-06 +date: 2022-09-07 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 bdf97d7d0d912..51cac2cd8dd38 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: 2022-09-06 +date: 2022-09-07 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 235d9d32ca150..5cdb8cd67bb19 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: 2022-09-06 +date: 2022-09-07 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 243a50e34daef..1632aa3fa7f31 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: 2022-09-06 +date: 2022-09-07 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 764c7684d7d46..df90be94bda7a 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: 2022-09-06 +date: 2022-09-07 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_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index b8acf29ea4e38..838dd0264b521 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: 2022-09-06 +date: 2022-09-07 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 a331ad12e0e93..cbe2ae22803d7 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: 2022-09-06 +date: 2022-09-07 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 97902b78c1e53..1b627550b4779 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: 2022-09-06 +date: 2022-09-07 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_crypto.mdx b/api_docs/kbn_crypto.mdx index 43df99de292a3..409492d14c605 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: 2022-09-06 +date: 2022-09-07 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 2a0a4c1f03b1c..04373ff4a990a 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index c32356270ffdd..df1e8f5ad7edd 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 97011ad26711a..02597e67dae12 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: 2022-09-06 +date: 2022-09-07 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 935cda637c96f..947a11055952c 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: 2022-09-06 +date: 2022-09-07 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 b32f51201616c..e4eb5dd27b8e8 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: 2022-09-06 +date: 2022-09-07 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 c47422872ebf4..c628dc8abac4c 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 6dbba50fa178c..3db1eec56982f 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: 2022-09-06 +date: 2022-09-07 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 38d420864d541..8d6a3e23a00bb 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 18b5ab724e4a0..3dc5d5692abc5 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index a910b0ed9ab1b..8d07a0dc99a4e 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: 2022-09-06 +date: 2022-09-07 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 e5218372534ea..963f6424de304 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: 2022-09-06 +date: 2022-09-07 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 f40b2ed2c2e69..b127dfc91df35 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index a64dad8da4aca..a68bb15370d0c 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 5cec3d061f0b3..5b97fa413ad5a 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: 2022-09-06 +date: 2022-09-07 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 bb92f973d42cd..d4d918c661bb9 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: 2022-09-06 +date: 2022-09-07 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_generate.mdx b/api_docs/kbn_generate.mdx index 5dfff84c00067..ee1a64701a459 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index 85bcb73c7ff82..9c1fa2178ef03 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index b72bd4ea22601..fd9eca7769507 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: 2022-09-06 +date: 2022-09-07 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 4ec02e4804950..227c70177e1e0 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 44cf43df05196..601f09298a638 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: 2022-09-06 +date: 2022-09-07 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 df5c65f7b4eda..2a9c1ba10b065 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: 2022-09-06 +date: 2022-09-07 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 e4c7ca4f21d55..62c887f4a3aa1 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 38220037bbaaa..6e015917e59ae 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 317a6ad2677df..189abd9254817 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: 2022-09-06 +date: 2022-09-07 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 49469e3bb12b8..cdf287835bea3 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: 2022-09-06 +date: 2022-09-07 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 1efda5e4370d1..bf42dcae5f89b 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_parser.mdx b/api_docs/kbn_kibana_manifest_parser.mdx index e6b10e49a9696..6e6ecb51a0452 100644 --- a/api_docs/kbn_kibana_manifest_parser.mdx +++ b/api_docs/kbn_kibana_manifest_parser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-parser title: "@kbn/kibana-manifest-parser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-parser plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-parser'] --- import kbnKibanaManifestParserObj from './kbn_kibana_manifest_parser.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 43ef3e3ba3e5e..83e80cab01f84 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index f1893197e6c1d..def8b60b0b717 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: 2022-09-06 +date: 2022-09-07 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 0457b5227adca..840df92d12bee 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: 2022-09-06 +date: 2022-09-07 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 4cccaf3476a66..16ff2f8b32459 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 4b0b781fb1b64..214b59337f863 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 6ce32fe93f5ea..1e8bee5a4a561 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: 2022-09-06 +date: 2022-09-07 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index cf4a767ab5c1d..27d9aaaac7cae 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: 2022-09-06 +date: 2022-09-07 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 71601f71a4b79..d51dc9688fa94 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index da9feeb48ae71..108507440c091 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 1c40770f1d14c..38b0399af6aeb 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: 2022-09-06 +date: 2022-09-07 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 9f81092cdb20a..d8f28e98bfe2b 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index dae280868ba9d..3b244a71ea63d 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: 2022-09-06 +date: 2022-09-07 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 1e780ff247591..09fbcf27b337b 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: 2022-09-06 +date: 2022-09-07 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 59df0a48f1476..b3d94adccca23 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 4bf64e81a2cd1..5370accff1c66 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index e1b169cb11910..f7345d4477ff1 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index c3e3fb68a1942..3b059008789cb 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 594a90c6b992b..a31ad37a982b2 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index d81e5aedca0cf..200f101ab449e 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: 2022-09-06 +date: 2022-09-07 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_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index b368ae1b07744..b6b49cb579487 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: 2022-09-06 +date: 2022-09-07 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 9ed642a17ac73..9089d14f4b003 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: 2022-09-06 +date: 2022-09-07 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 86bfe88fc4aeb..bba947ac7e11b 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: 2022-09-06 +date: 2022-09-07 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 03c96517daf9c..62be0bbfe48a3 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: 2022-09-06 +date: 2022-09-07 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 216e8655b5bb0..5d48b2224372c 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: 2022-09-06 +date: 2022-09-07 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 e366c255b21bc..7d2ae7dc2d211 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: 2022-09-06 +date: 2022-09-07 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 e1668dae928e1..0b8838d8d67a4 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: 2022-09-06 +date: 2022-09-07 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 5ab603d10b235..da2773dd82341 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: 2022-09-06 +date: 2022-09-07 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 0df64a065cf26..06d395a982f53 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: 2022-09-06 +date: 2022-09-07 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 9bd7ba6ecdcf1..9c0177542408b 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: 2022-09-06 +date: 2022-09-07 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 82de3d9623a24..135eacc8a0aba 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: 2022-09-06 +date: 2022-09-07 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 1a77dd0e57e72..e9d72aca33d1b 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: 2022-09-06 +date: 2022-09-07 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 22436ed83b27a..daec0fbafebba 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: 2022-09-06 +date: 2022-09-07 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 2c098b0e95e53..7caae7be65b1c 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index b5873d6efc302..eb2b36a093419 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.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 1b9969731fe0b..64ee09c6778d1 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: 2022-09-06 +date: 2022-09-07 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 4ddd242dcd879..e71fbfcc5e32e 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 2dbf643ce259a..c6517d1be1a1b 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 1c61826eb8bf3..ce09ae2ea9c5f 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: 2022-09-06 +date: 2022-09-07 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 8403639e1224a..b0c7ea475d617 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: 2022-09-06 +date: 2022-09-07 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index a40c2bfb5fcc4..5589729df6e7e 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: 2022-09-06 +date: 2022-09-07 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 f01fa624748e0..dd09c3dc356fc 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: 2022-09-06 +date: 2022-09-07 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 31f22196e83bb..c0ec200897412 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: 2022-09-06 +date: 2022-09-07 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 656be2f5e1328..2219b1673161e 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: 2022-09-06 +date: 2022-09-07 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 b60be077512db..25aba49a423f0 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: 2022-09-06 +date: 2022-09-07 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 9fd3f5c108992..9d22ea10ba60e 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: 2022-09-06 +date: 2022-09-07 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.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 3741a45a23382..130b3f49f7b49 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: 2022-09-06 +date: 2022-09-07 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 e4b72077198fd..7d23c47cc4602 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: 2022-09-06 +date: 2022-09-07 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 d23bd9668426f..0792196115fb6 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: 2022-09-06 +date: 2022-09-07 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 eefd75b0fb7fa..fcddeb97badf4 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: 2022-09-06 +date: 2022-09-07 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 df1fc4d91ed90..a58549e515ab0 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: 2022-09-06 +date: 2022-09-07 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 a1aa6cb442150..05e2e8a8fd607 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: 2022-09-06 +date: 2022-09-07 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 1895906126c41..9454edf932fc9 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: 2022-09-06 +date: 2022-09-07 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_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index d40071304bae9..f59cc2990735b 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: 2022-09-06 +date: 2022-09-07 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 3c001308fa58e..f4fa46e148800 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: 2022-09-06 +date: 2022-09-07 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 8ba8bdc804263..101e39460dd39 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 4ec7116e43707..bfdd757376a36 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index bf751e78e2f42..e5ba136cbd881 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index c6865cb9a191d..a0afadc32c950 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: 2022-09-06 +date: 2022-09-07 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 c72f3c4e12baa..e9d27d5fa7f3c 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: 2022-09-06 +date: 2022-09-07 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 f84cf0d75648c..7da16a7a86db4 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: 2022-09-06 +date: 2022-09-07 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 e82baeab81cb9..81a19853a94e8 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: 2022-09-06 +date: 2022-09-07 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 8a6da75a7fb10..a60e0bb196539 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: 2022-09-06 +date: 2022-09-07 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 ce97704328c60..0017ab4847b7a 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index b8e011b54f342..1c8a69ed3e281 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index b4ebcfae7c4b6..9097c0e23934f 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 8795b16250a89..d25870a0246af 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 5b8d2b1d3f0d9..ca24832d39054 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: 2022-09-06 +date: 2022-09-07 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index fc4fb9e4375b1..da182a99b439a 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index f1dc37b46d564..84ee7c476dc62 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: 2022-09-06 +date: 2022-09-07 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 fa7f7a31f609e..8b67a2571449f 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: 2022-09-06 +date: 2022-09-07 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 5a54f093e2c65..72ca020a5f4f1 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: 2022-09-06 +date: 2022-09-07 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 d7647c5a47b79..9a2fbe1f4d7ca 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index dbf3b91292d90..65de5938deab3 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: 2022-09-06 +date: 2022-09-07 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 c0cb483b562e5..0b5043451d275 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: 2022-09-06 +date: 2022-09-07 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 97fc5e134eb0f..2d0d88900d328 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: 2022-09-06 +date: 2022-09-07 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 a5b160dcf1979..f0691151cb965 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: 2022-09-06 +date: 2022-09-07 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 c5387dea37792..8c332abad5bed 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index cdb2181234b5f..acd735588a62a 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -6992,6 +6992,19 @@ "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, + { + "parentPluginId": "lens", + "id": "def-public.XYState.showCurrentTimeMarker", + "type": "CompoundType", + "tags": [], + "label": "showCurrentTimeMarker", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", + "deprecated": false + }, { "parentPluginId": "lens", "id": "def-public.XYState.valuesInLegend", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 698fa35e0d4e7..2dfde28af2161 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 613 | 0 | 527 | 41 | +| 614 | 0 | 528 | 41 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index affc63ce331b1..4d267c33db797 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: 2022-09-06 +date: 2022-09-07 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 2fcd6102f5a7a..61284932210e8 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: 2022-09-06 +date: 2022-09-07 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 e126958e9e916..736cddb1ca229 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: 2022-09-06 +date: 2022-09-07 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 962b40fc27f97..b031f5b679f4a 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 7a99638b5be97..1787ac033b8b5 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: 2022-09-06 +date: 2022-09-07 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 7e3a9628185ef..a73a0401d6142 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: 2022-09-06 +date: 2022-09-07 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 b7f0a7655b847..a9d2f93cef675 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: 2022-09-06 +date: 2022-09-07 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 35712c23b478a..d1fbb181e15aa 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: 2022-09-06 +date: 2022-09-07 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 ca77b7a91a941..10913f2ef46e9 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: 2022-09-06 +date: 2022-09-07 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 731ab5cc22e25..8841940f62ac3 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: 2022-09-06 +date: 2022-09-07 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 f23f4b5e01d53..1bd801357416c 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: 2022-09-06 +date: 2022-09-07 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 21c8675a744b8..ce8ecaf12b964 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 514e9d2af0390..79a9810bbeb51 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -7122,6 +7122,25 @@ "path": "x-pack/plugins/observability/server/routes/types.ts", "deprecated": false }, + { + "parentPluginId": "observability", + "id": "def-server.ObservabilityRouteHandlerResources.spacesService", + "type": "Object", + "tags": [], + "label": "spacesService", + "description": [], + "signature": [ + { + "pluginId": "spaces", + "scope": "server", + "docId": "kibSpacesPluginApi", + "section": "def-server.SpacesServiceStart", + "text": "SpacesServiceStart" + } + ], + "path": "x-pack/plugins/observability/server/routes/types.ts", + "deprecated": false + }, { "parentPluginId": "observability", "id": "def-server.ObservabilityRouteHandlerResources.request", @@ -7263,6 +7282,8 @@ "<\"POST /api/observability/slos\", ", "TypeC", "<{ body: ", + "IntersectionC", + "<[", "TypeC", "<{ name: ", "StringC", @@ -7360,7 +7381,13 @@ "TypeC", "<{ target: ", "NumberC", - "; }>; }>; }>, ", + "; }>; }>, ", + "PartialC", + "<{ settings: ", + "PartialC", + "<{ destination_index: ", + "StringC", + "; }>; }>]>; }>, ", { "pluginId": "observability", "scope": "server", @@ -7422,7 +7449,7 @@ "label": "ObservabilityConfig", "description": [], "signature": [ - "{ readonly unsafe: Readonly<{} & { slo: Readonly<{} & { enabled: boolean; }>; }>; readonly annotations: Readonly<{} & { index: string; enabled: boolean; }>; }" + "{ readonly unsafe: Readonly<{} & { slo: Readonly<{} & { enabled: boolean; }>; alertDetails: Readonly<{} & { enabled: boolean; }>; }>; readonly annotations: Readonly<{} & { index: string; enabled: boolean; }>; }" ], "path": "x-pack/plugins/observability/server/index.ts", "deprecated": false, @@ -7441,6 +7468,8 @@ "<\"POST /api/observability/slos\", ", "TypeC", "<{ body: ", + "IntersectionC", + "<[", "TypeC", "<{ name: ", "StringC", @@ -7538,7 +7567,13 @@ "TypeC", "<{ target: ", "NumberC", - "; }>; }>; }>, ", + "; }>; }>, ", + "PartialC", + "<{ settings: ", + "PartialC", + "<{ destination_index: ", + "StringC", + "; }>; }>]>; }>, ", { "pluginId": "observability", "scope": "server", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index fac3ee5ab3c80..6c8a1403db725 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 396 | 2 | 393 | 30 | +| 397 | 2 | 394 | 30 | ## Client diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 963eda18fc357..6ed5b1a6acec5 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: 2022-09-06 +date: 2022-09-07 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 4406258c16647..8fb222863c65f 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: 2022-09-06 +date: 2022-09-07 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 | |--------------|----------|------------------------| -| 442 | 368 | 36 | +| 444 | 369 | 36 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 30558 | 180 | 20413 | 972 | +| 30563 | 180 | 20418 | 973 | ## Plugin Directory @@ -31,7 +31,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 23 | 0 | 19 | 1 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 9 | 0 | 0 | 1 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 368 | 0 | 359 | 21 | -| | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 39 | 0 | 39 | 53 | +| | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 39 | 0 | 39 | 54 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 80 | 1 | 71 | 2 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | @@ -99,7 +99,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | kibanaUsageCollection | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 615 | 3 | 418 | 9 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 613 | 0 | 527 | 41 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 614 | 0 | 528 | 41 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -113,7 +113,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 34 | 0 | 34 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | -| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 396 | 2 | 393 | 30 | +| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 397 | 2 | 394 | 30 | | | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 13 | 0 | 13 | 0 | | painlessLab | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 243 | 2 | 187 | 12 | @@ -189,7 +189,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 18 | 0 | 0 | 0 | | | Kibana Core | - | 20 | 0 | 0 | 0 | | | [Owner missing] | - | 16 | 0 | 16 | 0 | -| | [Owner missing] | Elastic APM trace data generator | 75 | 0 | 75 | 12 | +| | [Owner missing] | Elastic APM trace data generator | 76 | 0 | 76 | 12 | | | [Owner missing] | - | 11 | 0 | 11 | 0 | | | [Owner missing] | - | 10 | 0 | 10 | 0 | | | [Owner missing] | - | 76 | 0 | 76 | 0 | @@ -288,6 +288,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 3 | 0 | 3 | 0 | | | Kibana Core | - | 5 | 0 | 0 | 0 | | | Kibana Core | - | 6 | 0 | 6 | 0 | +| | Kibana Core | - | 2 | 0 | 2 | 0 | | | Kibana Core | - | 94 | 1 | 66 | 0 | | | Kibana Core | - | 289 | 1 | 126 | 0 | | | Kibana Core | - | 68 | 0 | 49 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 7fb55d6a2ea42..a2d91355724e8 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 06e4c7b534cd6..514397f4332c4 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: 2022-09-06 +date: 2022-09-07 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 eda4e337d214e..da316fb620860 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: 2022-09-06 +date: 2022-09-07 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 74411201bbdbe..8e403ed011cdc 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: 2022-09-06 +date: 2022-09-07 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 528417dce9da8..9c1bdbf4b9f86 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: 2022-09-06 +date: 2022-09-07 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 3e74986c45337..0e9a085aceb39 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: 2022-09-06 +date: 2022-09-07 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 0728273a80d40..fb7725b09955a 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: 2022-09-06 +date: 2022-09-07 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 1dddbeff870f6..3331a1cd1d67d 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: 2022-09-06 +date: 2022-09-07 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 0257e69849de6..3f6aa6aea4603 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: 2022-09-06 +date: 2022-09-07 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 12c1c41dcf350..20f8bf584aee3 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: 2022-09-06 +date: 2022-09-07 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 5c3b22b466476..9913bdfb6eea6 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: 2022-09-06 +date: 2022-09-07 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 be9ec295c6f89..a76dc1e07c80f 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: 2022-09-06 +date: 2022-09-07 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 c866e4461df0d..fa3ef4253b26f 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: 2022-09-06 +date: 2022-09-07 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 55e225c99929f..c13960d63da4d 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: 2022-09-06 +date: 2022-09-07 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 b47390e54e629..2345b3fc39c9a 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 11192512cb016..94abe987c266d 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index b8fbf49f5f14c..40b7eb80a0859 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: 2022-09-06 +date: 2022-09-07 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 e3cd4dcce6ed8..0027dfe8d1a38 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: 2022-09-06 +date: 2022-09-07 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 8543671e08df9..23975075d9454 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: 2022-09-06 +date: 2022-09-07 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 03127a254c900..83bfab4e72942 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: 2022-09-06 +date: 2022-09-07 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 ed9f4c13f94fa..cb3f5b3db0061 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index dc22930eb237c..f1fe863db08b7 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: 2022-09-06 +date: 2022-09-07 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 a1601dcf08d76..394fa389290e9 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: 2022-09-06 +date: 2022-09-07 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 8fc5e8ef87ee2..f3bd907ce22dd 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: 2022-09-06 +date: 2022-09-07 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 7edfe584d3398..0a81e2cc07c4d 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: 2022-09-06 +date: 2022-09-07 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 3a3e106eed4f5..0cf966b634b7c 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 8c3375cd9adc8..011ea8d0d95ab 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 0a3686d2ae196..8dae68398be5e 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: 2022-09-06 +date: 2022-09-07 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 a0cfa085f84e9..b794c5c014de0 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: 2022-09-06 +date: 2022-09-07 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 040b336530148..2992905d08c2d 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: 2022-09-06 +date: 2022-09-07 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 4070f5232d118..e616cd4f4cea1 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: 2022-09-06 +date: 2022-09-07 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 cb114687d6f1f..6b6005a4b6dc2 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index a40b2083ff429..086e4e4fdfa1c 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 5a83d305fdb9d..2987c084a54a2 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: 2022-09-06 +date: 2022-09-07 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 ca5d1fd443820..a8fd92525c5db 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index db92454641100..1c2f622fecd29 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: 2022-09-06 +date: 2022-09-07 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 6d198b48f1535..9749547dcd8b5 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: 2022-09-06 +date: 2022-09-07 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 b1eb48b0c600d..ccdad23728da6 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: 2022-09-06 +date: 2022-09-07 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 25047939fe057..ef7c321c20a94 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: 2022-09-06 +date: 2022-09-07 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 083de23429f5a..4fa1efe9dd5ce 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: 2022-09-06 +date: 2022-09-07 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 0d34ed2680ec6..66c145e6cbbf8 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: 2022-09-06 +date: 2022-09-07 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 2de4de83b5c9e..dc124a90e3097 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: 2022-09-06 +date: 2022-09-07 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 c826aba082e91..1295a090b2e3a 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: 2022-09-06 +date: 2022-09-07 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 13db8ee610d41..b91b9bddda3c1 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: 2022-09-06 +date: 2022-09-07 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 a44a9f88effb6..27042f1122f70 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: 2022-09-06 +date: 2022-09-07 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 fac57b166ef54..10a8dda9c980f 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: 2022-09-06 +date: 2022-09-07 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 b6fe855de9250..0d2d7576f2bcd 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: 2022-09-06 +date: 2022-09-07 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 adc7b920f08a3..0db3e646e0ddf 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: 2022-09-06 +date: 2022-09-07 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 9fc1b40590470..44eaeaf3d77a8 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: 2022-09-06 +date: 2022-09-07 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/api/actions-and-connectors/create.asciidoc b/docs/api/actions-and-connectors/create.asciidoc index 0dccbd4597c6d..e8533e0561cce 100644 --- a/docs/api/actions-and-connectors/create.asciidoc +++ b/docs/api/actions-and-connectors/create.asciidoc @@ -36,9 +36,26 @@ vary depending on the connector type. For example: + -- // tag::connector-config[] -.Index connectors +.Config properties [%collapsible%open] ==== + +.{ibm-r} connectors +[%collapsible%open] +===== +`apiUrl`:: +(Required, string) The {ibm-r} instance URL. + +`orgId`:: +(Required, string) The {ibm-r} organization ID. + +For more information, refer to <>. +===== + +.Index connectors +[%collapsible%open] +===== + `executionTimeField`:: (Optional, string) Specifies a field that will contain the time the alert condition was detected. The default value is `null`. @@ -51,11 +68,11 @@ condition was detected. The default value is `null`. request. The default value is `false`. For more information, refer to <>. -==== +===== .{jira} connectors [%collapsible%open] -==== +===== `apiUrl`:: (Required, string) The {jira} instance URL. @@ -64,11 +81,139 @@ For more information, refer to <>. (Required, string) The {jira} project key. For more information, refer to <>. -==== +===== + +.{swimlane} connectors +[%collapsible%open] +===== +`apiUrl`:: +(Required, string) The {swimlane} instance URL. + +`appId`:: +(Required, string) The {swimlane} application ID. + +`connectorType`:: +(Required, String) The type of the connector. Valid values are: `all`, `alerts`, `cases`. + +`mappings`:: +(Optional, object) The field mapping. ++ +.Mappings properties +[%collapsible%open] +====== + +`alertIdConfig`::: +(Optional, object) Mapping for the alert ID. + +`fieldType`:::: +(Required, object) The type of the field in {swimlane}. + +`id`:::: +(Required, string) The id of the field in {swimlane}. + +`key`:::: +(Required, string) The key of the field in {swimlane}. + +`name`:::: +(Required, string) The name of the field in {swimlane}. + +`caseIdConfig`::: +(Optional, object) Mapping for the case ID. + +`fieldType`:::: +(Required, object) The type of the field in {swimlane}. + +`id`:::: +(Required, string) The id of the field in {swimlane}. + +`key`:::: +(Required, string) The key of the field in {swimlane}. + +`name`:::: +(Required, string) The name of the field in {swimlane}. + +`caseNameConfig`::: +(Optional, object) Mapping for the case name. + +`fieldType`:::: +(Required, object) The type of the field in {swimlane}. + +`id`:::: +(Required, string) The id of the field in {swimlane}. + +`key`:::: +(Required, string) The key of the field in {swimlane}. + +`name`:::: +(Required, string) The name of the field in {swimlane}. + +`commentsConfig`::: +(Optional, object) Mapping for the case comments. + +`fieldType`:::: +(Required, object) The type of the field in {swimlane}. + +`id`:::: +(Required, string) The id of the field in {swimlane}. + +`key`:::: +(Required, string) The key of the field in {swimlane}. + +`name`:::: +(Required, string) The name of the field in {swimlane}. + +`descriptionConfig`::: +(Optional, object) Mapping for the case description. + +`fieldType`:::: +(Required, object) The type of the field in {swimlane}. + +`id`:::: +(Required, string) The id of the field in {swimlane}. + +`key`:::: +(Required, string) The key of the field in {swimlane}. + +`name`:::: +(Required, string) The name of the field in {swimlane}. + +`ruleNameConfig`::: +(Optional, object) Mapping for the name of the alert's rule. + +`fieldType`:::: +(Required, Object) The type of the field in {swimlane}. + +`id`:::: +(Required, string) The id of the field in {swimlane}. + +`key`:::: +(Required, string) The key of the field in {swimlane}. + +`name`:::: +(Required, string) The name of the field in {swimlane}. + +`severityConfig`::: +(Optional, object) Mapping for the severity. + +`fieldType`:::: +(Required, object) The type of the field in {swimlane}. + +`id`:::: +(Required, string) The id of the field in {swimlane}. + +`key`:::: +(Required, string) The key of the field in {swimlane}. + +`name`:::: +(Required, string) The name of the field in {swimlane}. + +====== +For more information, refer to <>. +===== .{webhook-cm} connectors [%collapsible%open] -==== +===== `createCommentJson`:: (Optional, string) A JSON payload sent to the create comment URL to create a @@ -217,11 +362,12 @@ https://testing-jira.atlassian.net/browse/{{{external.system.title}}} ---- For more information, refer to <>. -==== +===== This object is not required for server log connectors. For more configuration properties, refer to <>. +==== // end::connector-config[] -- @@ -239,32 +385,51 @@ about the secrets configuration properties, refer to <>. + -- WARNING: Remember these values. You must provide them each time you call the <> API. --- -+ --- + // tag::connector-secrets[] -.{jira} connectors +.Secrets properties [%collapsible%open] ==== + +.{ibm-r} connectors +[%collapsible%open] +===== +`apiKeyId`:: +(Required, string) The authentication key ID for HTTP Basic authentication. + +`apiKeySecret`:: +(Required, string) The authentication key secret for HTTP Basic authentication. +===== + +.{jira} connectors +[%collapsible%open] +===== `apiToken`:: (Required, string) The {jira} API authentication token for HTTP basic authentication. `email`:: (Required, string) The account email for HTTP Basic authentication. -==== +===== + +.{swimlane} connectors +[%collapsible%open] +===== +`apiToken`:: +(string) {swimlane} API authentication token. +===== .{webhook-cm} connectors [%collapsible%open] -==== +===== `password`:: (Optional, string) The password for HTTP basic authentication. `user`:: (Optional, string) The username for HTTP basic authentication. -==== - +===== This object is not required for index or server log connectors. +==== // end::connector-secrets[] -- @@ -277,6 +442,8 @@ This object is not required for index or server log connectors. [[create-connector-api-example]] === {api-examples-title} +Create an index connector: + [source,sh] -------------------------------------------------- POST api/actions/connector @@ -308,3 +475,71 @@ The API returns the following: "is_missing_secrets": false } -------------------------------------------------- + +Create a {jira} connector: + +[source,sh] +-------------------------------------------------- +POST api/actions/connector +{ + "name": "my-jira-connector", + "connector_type_id": ".jira", + "config": { + "apiUrl": "https://elastic.atlassian.net", + "projectKey": "ES" + }, + "secrets": { + "email": "myEmail", + "apiToken": "myToken" + } +} +-------------------------------------------------- +// KIBANA + +Create an {ibm-r} connector: + +[source,sh] +-------------------------------------------------- +POST api/actions/connector +{ + "name": "my-resilient-connector", + "connector_type_id": ".resilient", + "config": { + "apiUrl": "https://elastic.resilient.net", + "orgId": "201" + }, + "secrets": { + "apiKeyId": "myKey", + "apiKeySecret": "myToken" + } +} +-------------------------------------------------- +// KIBANA + +Create a {swimlane} connector: + +[source,sh] +-------------------------------------------------- +POST api/actions/connector +{ + "name":"my-swimlane-connector", + "connector_type_id": ".swimlane", + "config":{ + "connectorType":"all", + "mappings":{ + "ruleNameConfig":{ + "id":"b6fst", + "name":"Alert Name", + "key":"alert-name", + "fieldType":"text" + } + }, + "appId":"myAppID", + "apiUrl":"https://myswimlaneinstance.com" + }, + "secrets":{ + "apiToken":"myToken" + } +} +-------------------------------------------------- +// KIBANA \ No newline at end of file diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index a3d6586e6e851..415080c12a65f 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -71,25 +71,24 @@ a| <> [NOTE] ============================================== Some connector types are paid commercial features, while others are free. -For a comparison of the Elastic subscription levels, -see {subscriptions}[the subscription page]. +For a comparison of the Elastic subscription levels, go to +{subscriptions}[the subscription page]. ============================================== [float] [[connector-management]] -=== Managing Connectors +=== Managing connectors -Rules use *Connectors* to route actions to different destinations like log files, ticketing systems, and messaging tools. While each {kib} app can offer their own types of rules, they typically share connectors. The *Connectors* tab offers a central place to view and manage all the connectors in the current space. - -For more information on connectors and the types of actions available see <>. +Rules use connectors to route actions to different destinations like log files, ticketing systems, and messaging tools. While each {kib} app can offer their own types of rules, they typically share connectors. The *Connectors* tab offers a central place to view and manage all the connectors in the current space. [role="screenshot"] -image::images/connector-listing.png[Example connector listing in the Rules and Connectors UI] +image::images/connector-listing.png[Example connector listing in the {rules-ui} UI] [float] === Required permissions -Access to connectors is granted based on your privileges to alerting-enabled features. See <> for more information. +Access to connectors is granted based on your privileges to alerting-enabled +features. For more information, go to <>. [float] === Connector networking configuration @@ -100,18 +99,15 @@ Use the <> to customize connecto [[connectors-list]] === Connector list -The *Connectors* tab lists all connectors in the current space. The *search bar* can be used to find specific connectors by name and/or type. - -[role="screenshot"] -image::images/connector-filter-by-search.png[Filtering the connector list using the search bar] - - -The *type* dropdown also lets you filter to a subset of connector types. +The *Connectors* tab lists all connectors in the current space. The search bar +can be used to find specific connectors by name and type. The *Type* dropdown +also enables you to filter to a subset of connector types. [role="screenshot"] image::images/connector-filter-by-type.png[Filtering the connector list by types of connectors] -You can delete individual connectors using the trash icon. Connectors can also be deleted in bulk by multi-selecting them and clicking the *Delete* button to the left of the search box. +You can delete individual connectors using the trash icon. Alternatively, select +multiple connectors and delete them in bulk using the *Delete* button. [role="screenshot"] image::images/connector-delete.png[Deleting connectors individually or in bulk] @@ -119,28 +115,35 @@ image::images/connector-delete.png[Deleting connectors individually or in bulk] [NOTE] ============================================================================ You can delete a connector even if there are still actions referencing it. -When this happens the action will fail to execute, and appear as errors in the {kib} logs. +When this happens the action will fail to run and errors appear in the {kib} logs. ============================================================================ [float] [[creating-new-connector]] === Creating a new connector -New connectors can be created by clicking the *Create connector* button, which will guide you to select the type of connector and configure its properties. Refer to <> for the types of connectors available and how to configure them. Once you create a connector it will be made available to you anytime you set up an action in the current space. +New connectors can be created with the *Create connector* button, which guides +you to select the type of connector and configure its properties. [role="screenshot"] image::images/connector-select-type.png[Connector select type] +After you create a connector, it is available for use any time you set up an +action in the current space. + [float] [[importing-and-exporting-connectors]] === Importing and exporting connectors -To import and export connectors, use the <>. -After a successful import, the proper banner is displayed: +To import and export connectors, use the +<>. + [role="screenshot"] -image::images/coonectors-import-banner.png[Connectors import banner, width=50%] +image::images/connectors-import-banner.png[Connectors import banner, width=50%] + +If a connector is missing sensitive information after the import, a **Fix** +button appears in *{rules-ui}*. -If a connector is missing user sensitive information because of the import, a **Fix** button appears in the list view. [role="screenshot"] image::images/connectors-with-missing-secrets.png[Connectors with missing secrets] @@ -156,7 +159,7 @@ before {kib} starts. === Monitoring connectors The <> helps you understand the performance of all tasks in your environment. -However, if connectors fail to execute, they will report as successful to Task Manager. The failure stats will not +However, if connectors fail to run, they will report as successful to Task Manager. The failure stats will not accurately depict the performance of connectors. For more information on connector successes and failures, refer to the <>. diff --git a/docs/management/connectors/images/connector-delete.png b/docs/management/connectors/images/connector-delete.png index ccb6bcea4bade..2e0e5d8a06b25 100644 Binary files a/docs/management/connectors/images/connector-delete.png and b/docs/management/connectors/images/connector-delete.png differ diff --git a/docs/management/connectors/images/connector-filter-by-search.png b/docs/management/connectors/images/connector-filter-by-search.png deleted file mode 100644 index 97348e70d91b3..0000000000000 Binary files a/docs/management/connectors/images/connector-filter-by-search.png and /dev/null differ diff --git a/docs/management/connectors/images/connector-filter-by-type.png b/docs/management/connectors/images/connector-filter-by-type.png index b95ef4e1a2e3e..c09a285d2af9c 100644 Binary files a/docs/management/connectors/images/connector-filter-by-type.png and b/docs/management/connectors/images/connector-filter-by-type.png differ diff --git a/docs/management/connectors/images/connector-listing.png b/docs/management/connectors/images/connector-listing.png index 68a529e16aa76..e7fb0899ef4a7 100644 Binary files a/docs/management/connectors/images/connector-listing.png and b/docs/management/connectors/images/connector-listing.png differ diff --git a/docs/management/connectors/images/connector-select-type.png b/docs/management/connectors/images/connector-select-type.png index ef5825b149311..90f26778dd957 100644 Binary files a/docs/management/connectors/images/connector-select-type.png and b/docs/management/connectors/images/connector-select-type.png differ diff --git a/docs/management/connectors/images/connectors-import-banner.png b/docs/management/connectors/images/connectors-import-banner.png new file mode 100644 index 0000000000000..92e4fe68d929d Binary files /dev/null and b/docs/management/connectors/images/connectors-import-banner.png differ diff --git a/docs/management/connectors/images/connectors-with-missing-secrets.png b/docs/management/connectors/images/connectors-with-missing-secrets.png new file mode 100644 index 0000000000000..f4f2ba0d73e13 Binary files /dev/null and b/docs/management/connectors/images/connectors-with-missing-secrets.png differ diff --git a/docs/management/images/connectors-with-missing-secrets.png b/docs/management/images/connectors-with-missing-secrets.png deleted file mode 100644 index ffc902d4a4768..0000000000000 Binary files a/docs/management/images/connectors-with-missing-secrets.png and /dev/null differ diff --git a/docs/management/images/coonectors-import-banner.png b/docs/management/images/coonectors-import-banner.png deleted file mode 100644 index 55a6e91d28c8d..0000000000000 Binary files a/docs/management/images/coonectors-import-banner.png and /dev/null differ diff --git a/docs/management/watcher-ui/index.asciidoc b/docs/management/watcher-ui/index.asciidoc index 3523fc3a9fc14..c4e5ef928b9b0 100644 --- a/docs/management/watcher-ui/index.asciidoc +++ b/docs/management/watcher-ui/index.asciidoc @@ -225,6 +225,10 @@ simulation. Be aware of these implementation details on overrides: * Condition overrides indicates if you want to force the condition to always be `true`. * Action overrides support {ref}/watcher-api-execute-watch.html#watcher-api-execute-watch-action-mode[multiple options]. +Some watches, such as those using basic auth credentials, contain passwords. For security +reasons, these passwords will be stripped from any watches that are loaded by the +Watcher UI. You will need to manually re-enter these passwords to simulate the watch. + After starting the simulation, you’ll see a results screen. For more information on the fields in the response, see the {ref}/watcher-api-execute-watch.html[Execute watch API]. diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index 9174995c5d9e1..941cfca92e603 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -162,6 +162,17 @@ Adds a message accessible at the login UI with additional help information for t xpack.security.authc.selector.enabled {ess-icon}:: Determines if the login selector UI should be enabled. By default, this setting is set to `true` if more than one authentication provider is configured. +[float] +[[authentication-access-agreement-settings]] +==== Configure a default access agreement + +You can configure the following settings in the `kibana.yml` file. + +xpack.security.accessAgreement.message {ess-icon}:: +This setting specifies the access agreement text in Markdown format that will be used as the default access agreement for all providers that do not +specify a value for `xpack.security.authc.providers...accessAgreement.message`. +For more information, refer to <>. + [float] [[security-session-and-cookie-settings]] ==== Session and cookie security settings diff --git a/docs/user/security/access-agreement.asciidoc b/docs/user/security/access-agreement.asciidoc index 9d9a0bb61a90b..03e5a312937a5 100644 --- a/docs/user/security/access-agreement.asciidoc +++ b/docs/user/security/access-agreement.asciidoc @@ -6,6 +6,9 @@ Access agreement is a https://www.elastic.co/subscriptions[subscription feature] agreement before accessing {kib}. The agreement text supports Markdown format and can be specified using the `xpack.security.authc.providers...accessAgreement.message` setting. +You can specify a default access agreement using the `xpack.security.accessAgreement.message` setting. +This message will be used for each provider who doesn't specify an access agreement. + [NOTE] ============================================================================ You need to acknowledge the access agreement only once per session, and {kib} reports the acknowledgement in the audit logs. diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index 40c4858d89f7d..b9fd0eef45c10 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -171,6 +171,9 @@ { "label": "Contributors Newsletters", "items": [ + { + "id": "kibAugust2022ContributorNewsletter" + }, { "id": "kibJuly2022ContributorNewsletter" }, diff --git a/package.json b/package.json index 7a59e569a7b4d..fa57a8c0d6f27 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "@dnd-kit/utilities": "^2.0.0", "@elastic/apm-rum": "^5.12.0", "@elastic/apm-rum-react": "^1.4.2", - "@elastic/charts": "47.2.1", + "@elastic/charts": "48.0.0", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.3.0-canary.1", "@elastic/ems-client": "8.3.3", @@ -250,6 +250,8 @@ "@kbn/core-preboot-server": "link:bazel-bin/packages/core/preboot/core-preboot-server", "@kbn/core-preboot-server-internal": "link:bazel-bin/packages/core/preboot/core-preboot-server-internal", "@kbn/core-preboot-server-mocks": "link:bazel-bin/packages/core/preboot/core-preboot-server-mocks", + "@kbn/core-rendering-browser-internal": "link:bazel-bin/packages/core/rendering/core-rendering-browser-internal", + "@kbn/core-rendering-browser-mocks": "link:bazel-bin/packages/core/rendering/core-rendering-browser-mocks", "@kbn/core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser", "@kbn/core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server", "@kbn/core-saved-objects-api-server-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server-internal", @@ -444,6 +446,7 @@ "deep-freeze-strict": "^1.1.1", "deepmerge": "^4.2.2", "del": "^6.1.0", + "elastic-apm-http-client": "^11.0.1", "elastic-apm-node": "^3.38.0", "email-addresses": "^5.0.0", "execa": "^4.0.2", @@ -934,6 +937,8 @@ "@types/kbn__core-preboot-server-internal": "link:bazel-bin/packages/core/preboot/core-preboot-server-internal/npm_module_types", "@types/kbn__core-preboot-server-mocks": "link:bazel-bin/packages/core/preboot/core-preboot-server-mocks/npm_module_types", "@types/kbn__core-public-internal-base": "link:bazel-bin/packages/core/public/internal-base/npm_module_types", + "@types/kbn__core-rendering-browser-internal": "link:bazel-bin/packages/core/rendering/core-rendering-browser-internal/npm_module_types", + "@types/kbn__core-rendering-browser-mocks": "link:bazel-bin/packages/core/rendering/core-rendering-browser-mocks/npm_module_types", "@types/kbn__core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser/npm_module_types", "@types/kbn__core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server/npm_module_types", "@types/kbn__core-saved-objects-api-server-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server-internal/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 79c8384b9fdaf..b5bd2f384d76b 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -115,6 +115,8 @@ filegroup( "//packages/core/preboot/core-preboot-server:build", "//packages/core/preboot/core-preboot-server-internal:build", "//packages/core/preboot/core-preboot-server-mocks:build", + "//packages/core/rendering/core-rendering-browser-internal:build", + "//packages/core/rendering/core-rendering-browser-mocks:build", "//packages/core/saved-objects/core-saved-objects-api-browser:build", "//packages/core/saved-objects/core-saved-objects-api-server:build", "//packages/core/saved-objects/core-saved-objects-api-server-internal:build", @@ -418,6 +420,8 @@ filegroup( "//packages/core/preboot/core-preboot-server:build_types", "//packages/core/preboot/core-preboot-server-internal:build_types", "//packages/core/preboot/core-preboot-server-mocks:build_types", + "//packages/core/rendering/core-rendering-browser-internal:build_types", + "//packages/core/rendering/core-rendering-browser-mocks:build_types", "//packages/core/saved-objects/core-saved-objects-api-browser:build_types", "//packages/core/saved-objects/core-saved-objects-api-server:build_types", "//packages/core/saved-objects/core-saved-objects-api-server-internal:build_types", diff --git a/packages/core/rendering/core-rendering-browser-internal/BUILD.bazel b/packages/core/rendering/core-rendering-browser-internal/BUILD.bazel new file mode 100644 index 0000000000000..e9f1ff1b1e19d --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-internal/BUILD.bazel @@ -0,0 +1,138 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-rendering-browser-internal" +PKG_REQUIRE_NAME = "@kbn/core-rendering-browser-internal" + +SOURCE_FILES = glob( + [ + "**/*.ts", + "**/*.tsx", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//react", + "@npm//react-dom", + "@npm//rxjs", + "@npm//classnames", + "@npm//react-use", + "//packages/core/application/core-application-common", + "//packages/core/theme/core-theme-browser-internal", + ### test dependencies + "//packages/core/application/core-application-browser-mocks", + "//packages/core/chrome/core-chrome-browser-mocks", + "//packages/core/overlays/core-overlays-browser-mocks", + "//packages/core/theme/core-theme-browser-mocks", + "//packages/core/i18n/core-i18n-browser-mocks", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/react", + "@npm//@types/classnames", + "@npm//@types/react-dom", + "@npm//rxjs", + "@npm//react-use", + "//packages/core/application/core-application-common:npm_module_types", + "//packages/core/application/core-application-browser-internal:npm_module_types", + "//packages/core/theme/core-theme-browser:npm_module_types", + "//packages/core/theme/core-theme-browser-internal:npm_module_types", + "//packages/core/i18n/core-i18n-browser:npm_module_types", + "//packages/core/overlays/core-overlays-browser:npm_module_types", + "//packages/core/chrome/core-chrome-browser-internal:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/rendering/core-rendering-browser-internal/README.md b/packages/core/rendering/core-rendering-browser-internal/README.md new file mode 100644 index 0000000000000..abdcdb2906957 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-internal/README.md @@ -0,0 +1,4 @@ +# @kbn/core-rendering-browser-internal + +This package contains the internal types and implementation for Core's browser-side rendering service. + diff --git a/packages/core/rendering/core-rendering-browser-internal/index.ts b/packages/core/rendering/core-rendering-browser-internal/index.ts new file mode 100644 index 0000000000000..52507be5106d2 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-internal/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { RenderingService } from './src'; diff --git a/packages/core/rendering/core-rendering-browser-internal/jest.config.js b/packages/core/rendering/core-rendering-browser-internal/jest.config.js new file mode 100644 index 0000000000000..2e15197b4b8a6 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-internal/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/core/rendering/core-rendering-browser-internal'], +}; diff --git a/packages/core/rendering/core-rendering-browser-internal/package.json b/packages/core/rendering/core-rendering-browser-internal/package.json new file mode 100644 index 0000000000000..78cf06e44c4dc --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-internal/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/core-rendering-browser-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/public/rendering/app_containers.test.tsx b/packages/core/rendering/core-rendering-browser-internal/src/app_containers.test.tsx similarity index 100% rename from src/core/public/rendering/app_containers.test.tsx rename to packages/core/rendering/core-rendering-browser-internal/src/app_containers.test.tsx diff --git a/src/core/public/rendering/app_containers.tsx b/packages/core/rendering/core-rendering-browser-internal/src/app_containers.tsx similarity index 100% rename from src/core/public/rendering/app_containers.tsx rename to packages/core/rendering/core-rendering-browser-internal/src/app_containers.tsx diff --git a/src/core/public/rendering/index.ts b/packages/core/rendering/core-rendering-browser-internal/src/index.ts similarity index 100% rename from src/core/public/rendering/index.ts rename to packages/core/rendering/core-rendering-browser-internal/src/index.ts diff --git a/src/core/public/rendering/rendering_service.test.tsx b/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.test.tsx similarity index 100% rename from src/core/public/rendering/rendering_service.test.tsx rename to packages/core/rendering/core-rendering-browser-internal/src/rendering_service.test.tsx index 9c63fea250c38..5dcde921837b1 100644 --- a/src/core/public/rendering/rendering_service.test.tsx +++ b/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.test.tsx @@ -10,12 +10,12 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { BehaviorSubject } from 'rxjs'; -import { RenderingService } from './rendering_service'; import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; import { chromeServiceMock } from '@kbn/core-chrome-browser-mocks'; import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; import { themeServiceMock } from '@kbn/core-theme-browser-mocks'; import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks'; +import { RenderingService } from './rendering_service'; describe('RenderingService#start', () => { let application: ReturnType; diff --git a/src/core/public/rendering/rendering_service.tsx b/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx similarity index 100% rename from src/core/public/rendering/rendering_service.tsx rename to packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx diff --git a/packages/core/rendering/core-rendering-browser-internal/tsconfig.json b/packages/core/rendering/core-rendering-browser-internal/tsconfig.json new file mode 100644 index 0000000000000..2249e2ee93761 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-internal/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ] +} diff --git a/packages/core/rendering/core-rendering-browser-mocks/BUILD.bazel b/packages/core/rendering/core-rendering-browser-mocks/BUILD.bazel new file mode 100644 index 0000000000000..7493624fa2e65 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/BUILD.bazel @@ -0,0 +1,114 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-rendering-browser-mocks" +PKG_REQUIRE_NAME = "@kbn/core-rendering-browser-mocks" + +SOURCE_FILES = glob( + [ + "**/*.ts", + "**/*.tsx", + ], + exclude = [ + "**/*.config.js", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/kbn-utility-types:npm_module_types", + "//packages/core/rendering/core-rendering-browser-internal:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/rendering/core-rendering-browser-mocks/README.md b/packages/core/rendering/core-rendering-browser-mocks/README.md new file mode 100644 index 0000000000000..d4a495f84134c --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/README.md @@ -0,0 +1,4 @@ +# @kbn/core-rendering-browser-mocks + +This package contains mocks for Core's browser-side rendering service. +- `renderingServiceMock` \ No newline at end of file diff --git a/packages/core/rendering/core-rendering-browser-mocks/index.ts b/packages/core/rendering/core-rendering-browser-mocks/index.ts new file mode 100644 index 0000000000000..e7ce72ce797fb --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { renderingServiceMock } from './src'; diff --git a/packages/core/rendering/core-rendering-browser-mocks/jest.config.js b/packages/core/rendering/core-rendering-browser-mocks/jest.config.js new file mode 100644 index 0000000000000..f9e233526e63a --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/core/rendering/core-rendering-browser-mocks'], +}; diff --git a/packages/core/rendering/core-rendering-browser-mocks/package.json b/packages/core/rendering/core-rendering-browser-mocks/package.json new file mode 100644 index 0000000000000..525a131a56f61 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/core-rendering-browser-mocks", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/rendering/core-rendering-browser-mocks/src/index.ts b/packages/core/rendering/core-rendering-browser-mocks/src/index.ts new file mode 100644 index 0000000000000..d56794e3e9ac6 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/src/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { renderingServiceMock } from './rendering_service.mock'; diff --git a/src/core/public/rendering/rendering_service.mock.ts b/packages/core/rendering/core-rendering-browser-mocks/src/rendering_service.mock.ts similarity index 89% rename from src/core/public/rendering/rendering_service.mock.ts rename to packages/core/rendering/core-rendering-browser-mocks/src/rendering_service.mock.ts index 28d79de8bb043..fa1965a25a15f 100644 --- a/src/core/public/rendering/rendering_service.mock.ts +++ b/packages/core/rendering/core-rendering-browser-mocks/src/rendering_service.mock.ts @@ -7,9 +7,10 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; -import { RenderingService } from './rendering_service'; +import type { RenderingService } from '@kbn/core-rendering-browser-internal'; type RenderingServiceContract = PublicMethodsOf; + const createMock = () => { const mocked: jest.Mocked = { start: jest.fn(), diff --git a/packages/core/rendering/core-rendering-browser-mocks/tsconfig.json b/packages/core/rendering/core-rendering-browser-mocks/tsconfig.json new file mode 100644 index 0000000000000..26b4c7aca3a67 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ] +} diff --git a/packages/kbn-apm-synthtrace/BUILD.bazel b/packages/kbn-apm-synthtrace/BUILD.bazel index b3cd2939135de..2f87b86044915 100644 --- a/packages/kbn-apm-synthtrace/BUILD.bazel +++ b/packages/kbn-apm-synthtrace/BUILD.bazel @@ -47,6 +47,7 @@ RUNTIME_DEPS = [ "@npm//yargs", "@npm//node-fetch", "@npm//semver", + "@npm//elastic-apm-http-client", ] TYPES_DEPS = [ diff --git a/packages/kbn-apm-synthtrace/README.md b/packages/kbn-apm-synthtrace/README.md index 3afc25fb7e9a8..dcd50215c6a85 100644 --- a/packages/kbn-apm-synthtrace/README.md +++ b/packages/kbn-apm-synthtrace/README.md @@ -155,3 +155,19 @@ Note: | `--logLevel` | [enum] | `info` | Log level | | `--gcpRepository` | [string] | | Allows you to register a GCP repository in :[:base_path] format | | `-p` | [string] | | Specify multiple sets of streamaggregators to be included in the StreamProcessor | + +## Testing + +Run the Jest tests: + +``` +node scripts/jest --config ./packages/kbn-apm-synthtrace/jest.config.js +``` + +## Typescript + +Run the type checker: + +``` +node scripts/type_check.js --project packages/kbn-apm-synthtrace/tsconfig.json +``` diff --git a/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts b/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts index 4fd9167052def..ecfbd4a387f34 100644 --- a/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts +++ b/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts @@ -32,6 +32,11 @@ function options(y: Argv) { describe: 'Kibana target, used to bootstrap datastreams/mappings/templates/settings', string: true, }) + .option('apm', { + describe: + 'APM Server target. Send data to APM over the intake API instead of generating ES documents', + string: true, + }) .option('cloudId', { describe: 'Provide connection information and will force APM on the cloud to migrate to run as a Fleet integration', @@ -98,6 +103,11 @@ function options(y: Argv) { describe: 'Force writing to legacy indices', boolean: true, }) + .option('skipPackageInstall', { + describe: 'Skip automatically installing the package', + boolean: true, + default: false, + }) .option('scenarioOpts', { describe: 'Options specific to the scenario', coerce: (arg) => { @@ -144,7 +154,7 @@ export function runSynthtrace() { const runOptions = parseRunCliFlags(argv); - const { logger, apmEsClient } = getCommonServices(runOptions); + const { logger, apmEsClient, apmIntakeClient } = getCommonServices(runOptions); const toMs = datemath.parse(String(argv.to ?? 'now'))!.valueOf(); const to = new Date(toMs); @@ -181,12 +191,14 @@ export function runSynthtrace() { kibanaUrl = await kibanaClient.discoverLocalKibana(); } if (!kibanaUrl) throw Error('kibanaUrl could not be determined'); - await kibanaClient.installApmPackage( - kibanaUrl, - version, - runOptions.username, - runOptions.password - ); + if (!argv.skipPackageInstall) { + await kibanaClient.installApmPackage( + kibanaUrl, + version, + runOptions.username, + runOptions.password + ); + } } } @@ -210,7 +222,11 @@ export function runSynthtrace() { } } if (argv.clean) { - await apmEsClient.clean(aggregators.map((a) => a.getDataStreamName() + '-*')); + if (argv.apm) { + await apmEsClient.clean(['metrics-apm.service-*']); + } else { + await apmEsClient.clean(aggregators.map((a) => a.getDataStreamName() + '-*')); + } } if (runOptions.gcpRepository) { await apmEsClient.registerGcpRepository(runOptions.gcpRepository); @@ -234,7 +250,7 @@ export function runSynthtrace() { await startHistoricalDataUpload(apmEsClient, logger, runOptions, from, to, version); if (live) { - await startLiveDataUpload(apmEsClient, logger, runOptions, to, version); + await startLiveDataUpload(apmEsClient, apmIntakeClient, logger, runOptions, to, version); } } ) diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/get_common_services.ts b/packages/kbn-apm-synthtrace/src/cli/utils/get_common_services.ts index e39aa9fa7112e..c51b435c15c32 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/get_common_services.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/get_common_services.ts @@ -7,6 +7,7 @@ */ import { Client, ClientOptions } from '@elastic/elasticsearch'; +import { ApmSynthtraceApmClient } from '../../lib/apm/client/apm_synthtrace_apm_client'; import { ApmSynthtraceEsClient } from '../../lib/apm/client/apm_synthtrace_es_client'; import { createLogger, Logger } from '../../lib/utils/create_logger'; import { RunOptions } from './parse_run_cli_flags'; @@ -16,7 +17,7 @@ export function getLogger({ logLevel }: RunOptions) { } export function getCommonServices( - { target, cloudId, username, password, logLevel, forceLegacyIndices }: RunOptions, + { target, cloudId, apm, username, password, logLevel, forceLegacyIndices }: RunOptions, logger?: Logger ) { if (!target && !cloudId) { @@ -44,10 +45,12 @@ export function getCommonServices( forceLegacyIndices, refreshAfterIndex: false, }); + const apmIntakeClient = apm ? new ApmSynthtraceApmClient(apm, logger) : null; return { logger, apmEsClient, + apmIntakeClient, }; } diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/parse_run_cli_flags.ts b/packages/kbn-apm-synthtrace/src/cli/utils/parse_run_cli_flags.ts index 7b873ee480f90..ce4c10931278a 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/parse_run_cli_flags.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/parse_run_cli_flags.ts @@ -66,6 +66,7 @@ export function parseRunCliFlags(flags: RunCliFlags) { 'maxDocs', 'maxDocsConfidence', 'target', + 'apm', 'cloudId', 'username', 'password', diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/start_historical_data_upload.ts b/packages/kbn-apm-synthtrace/src/cli/utils/start_historical_data_upload.ts index 093a34914bbe9..99396c28dfdd7 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/start_historical_data_upload.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/start_historical_data_upload.ts @@ -54,7 +54,7 @@ export async function startHistoricalDataUpload( } const events = logger.perf('generate_scenario', () => generate({ from, to })); - const ratePerMinute = events.ratePerMinute(); + const ratePerMinute = events.estimatedRatePerMinute(); logger.info( `Scenario is generating ${ratePerMinute.toLocaleString()} events per minute interval` ); @@ -62,7 +62,7 @@ export async function startHistoricalDataUpload( if (runOptions.maxDocs) { // estimate a more accurate range end for when --maxDocs is specified rangeEnd = moment(from) - // ratePerMinute() is not exact if the generator is yielding variable documents + // estimatedRatePerMinute() is not exact if the generator is yielding variable documents // the rate is calculated by peeking the first yielded event and its children. // for real complex cases manually specifying --to is encouraged. .subtract((runOptions.maxDocs / ratePerMinute) * runOptions.maxDocsConfidence, 'm') @@ -211,7 +211,7 @@ export async function startHistoricalDataUpload( return Promise.all(workers.map((worker) => limiter(() => worker()))) .then(async () => { if (!runOptions.dryRun) { - await esClient.refresh(); + await esClient.refresh(runOptions.apm ? ['metrics-apm.service-*'] : []); } }) .then(() => { diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts b/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts index 154b804626c03..6615d16aa91a9 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts @@ -14,9 +14,11 @@ import { ApmSynthtraceEsClient } from '../../lib/apm'; import { Logger } from '../../lib/utils/create_logger'; import { EntityArrayIterable } from '../../lib/entity_iterable'; import { StreamProcessor } from '../../lib/stream_processor'; +import { ApmSynthtraceApmClient } from '../../lib/apm/client/apm_synthtrace_apm_client'; export async function startLiveDataUpload( esClient: ApmSynthtraceEsClient, + apmIntakeClient: ApmSynthtraceApmClient | null, logger: Logger, runOptions: RunOptions, start: Date, @@ -41,7 +43,7 @@ export async function startLiveDataUpload( generate({ from: bucketFrom, to: bucketTo }).toArray() ); - logger.debug( + logger.info( `Requesting ${new Date(bucketFrom).toISOString()} to ${new Date( bucketTo ).toISOString()}, events: ${nextEvents.length}` @@ -65,18 +67,20 @@ export async function startLiveDataUpload( maxSourceEvents: runOptions.maxDocs, name: `Live index`, }); - await logger.perf('index_live_scenario', () => - esClient.index( - new EntityArrayIterable(eventsToUpload), - { - concurrency: runOptions.workers, - maxDocs: runOptions.maxDocs, - mapToIndex, - dryRun: false, - }, - streamProcessor - ) - ); + await logger.perf('index_live_scenario', async () => { + const events = new EntityArrayIterable(eventsToUpload); + const streamToBulkOptions = { + concurrency: runOptions.workers, + maxDocs: runOptions.maxDocs, + mapToIndex, + dryRun: false, + }; + if (apmIntakeClient) { + await apmIntakeClient.index(events, streamToBulkOptions, streamProcessor); + } else { + await esClient.index(events, streamToBulkOptions, streamProcessor); + } + }); } do { diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts b/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts index af075d0652c34..720b1b0527e80 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts @@ -38,7 +38,7 @@ export interface WorkerData { const { bucketFrom, bucketTo, runOptions, workerIndex, version } = workerData as WorkerData; -const { logger, apmEsClient } = getCommonServices(runOptions, l); +const { logger, apmEsClient, apmIntakeClient } = getCommonServices(runOptions, l); const file = runOptions.file; let scenario: Scenario; let events: EntityIterable; @@ -64,10 +64,11 @@ async function setup() { } }; const aggregators: StreamAggregator[] = [new ServiceLatencyAggregator()]; + // If we are sending data to apm-server we do not have to create any aggregates in the stream processor streamProcessor = new StreamProcessor({ version, - processors: StreamProcessor.apmProcessors, - streamAggregators: aggregators, + processors: apmIntakeClient ? [] : StreamProcessor.apmProcessors, + streamAggregators: apmIntakeClient ? [] : aggregators, maxSourceEvents: runOptions.maxDocs, logger: l, processedCallback: (processedDocuments) => { @@ -78,10 +79,13 @@ async function setup() { } async function doWork() { - await logger.perf( - 'index_scenario', - async () => await apmEsClient.index(events, streamToBulkOptions, streamProcessor) - ); + await logger.perf('index_scenario', async () => { + if (apmIntakeClient) { + await apmIntakeClient.index(events, streamToBulkOptions, streamProcessor); + } else { + await apmEsClient.index(events, streamToBulkOptions, streamProcessor); + } + }); } parentPort!.on('message', async (message) => { diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/apm_fields.ts b/packages/kbn-apm-synthtrace/src/lib/apm/apm_fields.ts index b5181e80a92e9..690e6c3563f27 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/apm_fields.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/apm_fields.ts @@ -32,6 +32,7 @@ export interface ApmException { message: string; } export interface Observer { + type: string; version: string; version_major: number; } @@ -42,6 +43,8 @@ export type ApmFields = Fields & 'agent.name': string; 'agent.version': string; 'container.id': string; + 'destination.address': string; + 'destination.port': number; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; @@ -75,6 +78,8 @@ export type ApmFields = Fields & 'service.runtime.name': string; 'service.runtime.version': string; 'service.framework.name': string; + 'service.target.name': string; + 'service.target.type': string; 'span.id': string; 'span.name': string; 'span.type': string; diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/base_span.ts b/packages/kbn-apm-synthtrace/src/lib/apm/base_span.ts index fa57c2871d8a8..f8c058592a494 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/base_span.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/base_span.ts @@ -76,4 +76,12 @@ export class BaseSpan extends Serializable { isTransaction(): this is Transaction { return this.fields['processor.event'] === 'transaction'; } + + labels(labels: Record) { + Object.entries(labels).forEach(([key, value]) => { + // @ts-expect-error + this.fields[`labels.${key}`] = value; + }); + return this; + } } diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_apm_client.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_apm_client.ts new file mode 100644 index 0000000000000..da836cd8a2119 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_apm_client.ts @@ -0,0 +1,346 @@ +/* + * Copyright 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 Client from 'elastic-apm-http-client'; +import Util from 'util'; +import { Logger } from '../../utils/create_logger'; +import { ApmFields } from '../apm_fields'; +import { EntityIterable } from '../../entity_iterable'; +import { StreamProcessor } from '../../stream_processor'; +import { EntityStreams } from '../../entity_streams'; +import { Fields } from '../../entity'; +import { Span } from './intake_v2/span'; +import { Error } from './intake_v2/error'; +import { Metadata } from './intake_v2/metadata'; +import { Transaction } from './intake_v2/transaction'; + +export interface StreamToBulkOptions { + concurrency?: number; + // the maximum number of documents to process + maxDocs?: number; + // the number of documents to flush the bulk operation defaults to 10k + flushInterval?: number; + mapToIndex?: (document: Record) => string; + dryRun: boolean; + itemStartStopCallback?: (item: TFields | null, done: boolean) => void; +} + +export interface ApmSynthtraceApmClientOptions { + forceLegacyIndices?: boolean; + // defaults to true if unspecified + refreshAfterIndex?: boolean; +} + +interface ClientState { + client: Client; + enqueued: number; + sendSpan: (s: Span) => Promise; + sendTransaction: (s: Transaction) => Promise; + sendError: (e: Error) => Promise; + flush: (o: any) => Promise; +} +interface ClientStats { + numEvents: number; + numEventsDropped: number; + numEventsEnqueued: number; + numEventsSent: number; + slowWriteBatch: number; + backoffReconnectCount: number; +} +export class ApmSynthtraceApmClient { + private readonly _serviceClients: Map = new Map(); + constructor( + private readonly apmTarget: string, + private readonly logger: Logger, + options?: ApmSynthtraceApmClientOptions + ) {} + + map(fields: ApmFields): [Span | Transaction, Error[]] { + const set = ( + key: T, + context: NonNullable, + setter: (context: NonNullable, value: NonNullable) => void + ) => { + if (fields[key]) { + setter(context, fields[key]!); + } + }; + const metadata: Metadata = { + service: { + name: fields['service.name'] ?? 'unknown', + agent: { + name: fields['agent.name'] ?? 'unknown', + version: fields['agent.version'] ?? 'unknown', + }, + }, + }; + + const system = (metadata.system = metadata.system ?? {}); + const container = (system.container = system.container ?? {}); + const kubernetes = (system.kubernetes = system.kubernetes ?? {}); + const pod = (kubernetes.pod = kubernetes.pod ?? {}); + set('container.id', container, (c, v) => (c.id = v)); + set('host.name', system, (c, v) => (c.hostname = v)); + set('host.hostname', system, (c, v) => (c.configured_hostname = v)); + set('kubernetes.pod.name', pod, (c, v) => (c.name = v)); + set('kubernetes.pod.uid', pod, (c, v) => (c.uid = v)); + + const e: Span | Transaction = fields['span.id'] + ? { + kind: 'span', + duration: fields['span.duration.us'] ?? 0, + id: fields['span.id'] ?? '', + name: fields['span.name'] ?? 'unknown', + parent_id: fields['parent.id'] ?? '', + type: fields['span.type'] ?? '', + timestamp: Math.trunc((fields['@timestamp'] ?? 0) * 1000), + trace_id: fields['trace.id'] ?? '', + } + : { + kind: 'transaction', + timestamp: Math.trunc((fields['@timestamp'] ?? 0) * 1000), + duration: fields['transaction.duration.us'] ?? 0, + id: fields['transaction.id'] ?? '', + type: fields['transaction.type'] ?? '', + trace_id: fields['trace.id'] ?? '', + span_count: { dropped: null, started: 0 }, + }; + + set('trace.id', e, (c, v) => (c.trace_id = v)); + set('parent.id', e, (c, v) => (c.parent_id = v)); + set( + 'span.links', + e, + (c, v) => (c.links = v.map((l) => ({ span_id: l.span?.id, trace_id: l.span?.id }))) + ); + + e.context = {}; + const service = (e.context.service = e.context.service ?? {}); + set('service.name', service, (c, v) => (c.name = v)); + set('service.version', service, (c, v) => (c.version = v)); + set('service.environment', service, (c, v) => (c.environment = v)); + const node = (service.node = service.node ?? {}); + set('service.node.name', node, (c, v) => (c.configured_name = v)); + const agent = (service.agent = service.agent ?? {}); + set('agent.name', agent, (c, v) => (c.name = v)); + set('agent.version', agent, (c, v) => (c.version = v)); + const runtime = (service.runtime = service.runtime ?? {}); + set('service.runtime.name', runtime, (c, v) => (c.name = v)); + set('service.runtime.version', runtime, (c, v) => (c.version = v)); + const framework = (service.framework = service.framework ?? {}); + set('service.framework.name', framework, (c, v) => (c.name = v)); + + set( + 'event.outcome', + e, + (c, v) => (c.outcome = v === 'failure' ? 'failure' : v === 'success' ? 'success' : 'unknown') + ); + + if (e.kind === 'span') { + set('span.duration.us', e, (c, v) => (c.duration = v / 1000)); + set('span.type', e, (c, v) => (c.type = v)); + set('span.subtype', e, (c, v) => (c.subtype = v)); + + const destination = (e.context.destination = e.context.destination ?? {}); + const destinationService = (destination.service = destination.service ?? { resource: '' }); + set('span.destination.service.name', destinationService, (c, v) => (c.name = v)); + set('span.destination.service.resource', destinationService, (c, v) => (c.resource = v)); + set('span.destination.service.type', destinationService, (c, v) => (c.type = v)); + } + if (e.kind === 'transaction') { + set('transaction.name', e, (c, v) => (c.name = v)); + set('transaction.type', e, (c, v) => (c.type = v)); + set('transaction.id', e, (c, v) => (c.id = v)); + set('transaction.duration.us', e, (c, v) => (c.duration = v / 1000)); + set('transaction.sampled', e, (c, v) => (c.sampled = v)); + } + + let errors: Error[] = []; + if (fields['error.id']) { + const exceptions = fields['error.exception'] ?? []; + errors = exceptions.map((ex) => { + const err: Error = { + id: '0', + timestamp: Math.trunc((fields['@timestamp'] ?? 0) * 1000), + context: e.context, + }; + set('error.id', err, (c, v) => (c.id = v)); + set('parent.id', err, (c, v) => (c.parent_id = v)); + set('trace.id', err, (c, v) => (c.trace_id = v)); + set('transaction.id', err, (c, v) => (c.transaction_id = v)); + set('error.grouping_name', err, (c, v) => (c.culprit = v)); + err.exception = { + message: ex.message, + type: 'Exception', + }; + if (!err.parent_id) err.parent_id = err.transaction_id ?? err.trace_id; + return err; + }); + } + + // TODO include event more context + // 'cloud.provider': string; + // 'cloud.project.name': string; + // 'cloud.service.name': string; + // 'cloud.availability_zone': string; + // 'cloud.machine.type': string; + // 'cloud.region': string; + // 'host.os.platform': string; + // 'faas.id': string; + // 'faas.coldstart': boolean; + // 'faas.execution': string; + // 'faas.trigger.type': string; + // 'faas.trigger.request_id': string; + + return [e, errors]; + } + + async index( + events: EntityIterable | Array>, + options?: StreamToBulkOptions, + streamProcessor?: StreamProcessor + ) { + const dataStream = Array.isArray(events) ? new EntityStreams(events) : events; + const sp = + streamProcessor != null + ? streamProcessor + : new StreamProcessor({ + processors: [], + maxSourceEvents: options?.maxDocs, + logger: this.logger, + }); + + let yielded = 0; + let fields: ApmFields | null = null; + // intentionally leaks `fields` so it can be pushed to callback events + const sideEffectYield = () => + sp.streamToDocumentAsync((e) => { + fields = e; + return this.map(e); + }, dataStream); + + if (options?.dryRun) { + await this.logger.perf('enumerate_scenario', async () => { + // @ts-ignore + // We just want to enumerate + for await (const item of sideEffectYield()) { + if (yielded === 0) { + options.itemStartStopCallback?.apply(this, [fields, false]); + yielded++; + } + } + options.itemStartStopCallback?.apply(this, [fields, true]); + }); + return; + } + const queueSize = 10000; + for await (const [item, _] of sideEffectYield()) { + if (item == null) continue; + + const service = item.context?.service?.name ?? 'unknown'; + const hostName = fields ? fields['host.name'] : 'unknown'; + // TODO evaluate if we really need service specific clients + // const lookup = `${service}::${hostName}`; + const lookup = `constant_key::1`; + if (!this._serviceClients.has(lookup)) { + const client = new Client({ + userAgent: `apm-agent-synthtrace/${sp.version}`, + serverUrl: this.apmTarget, + maxQueueSize: queueSize, + bufferWindowSize: queueSize / 2, + + serviceName: service, + serviceNodeName: service, + agentName: 'synthtrace', + agentVersion: sp.version, + serviceVersion: item.context?.service?.version ?? sp.version, + frameworkName: item.context?.service?.framework?.name ?? undefined, + frameworkVersion: item.context?.service?.framework?.version ?? undefined, + hostname: hostName, + }); + this._serviceClients.set(lookup, { + client, + enqueued: 0, + sendSpan: Util.promisify(client.sendSpan).bind(client), + sendTransaction: Util.promisify(client.sendTransaction).bind(client), + sendError: Util.promisify(client.sendError).bind(client), + flush: Util.promisify(client.flush).bind(client), + }); + } + const clientState = this._serviceClients.get(lookup)!; + + if (yielded === 0) { + options?.itemStartStopCallback?.apply(this, [fields, false]); + } + if (item.kind === 'span') { + clientState.sendSpan(item); + } else if (item.kind === 'transaction') { + clientState.sendTransaction(item); + } + yielded++; + clientState.enqueued++; + /* TODO finish implementing sending errors + errors.forEach((e) => { + clientState.sendError(e); + clientState.enqueued++; + });*/ + if (clientState.enqueued % queueSize === 0) { + this.logger.debug( + ` -- ${sp.name} Flushing client: ${lookup} after enqueueing ${clientState.enqueued}` + ); + await clientState.flush({}); + } + } + for (const [, state] of this._serviceClients) { + await state.flush({}); + } + // this attempts to group similar service names together for cleaner reporting + const totals = Array.from(this._serviceClients).reduce((p, c, i, a) => { + const serviceName = c[0].split('::')[0].replace(/-\d+$/, ''); + if (!p.has(serviceName)) { + p.set(serviceName, { enqueued: 0, sent: 0, names: new Set() }); + } + const s = p.get(serviceName)!; + s.enqueued += c[1].enqueued; + s.sent += c[1].client.sent; + s.names.add(c[0]); + const stats = c[1].client._getStats(); + if (!s.stats) { + s.stats = stats; + } else { + s.stats.backoffReconnectCount += stats.backoffReconnectCount; + s.stats.numEvents += stats.numEvents; + s.stats.numEventsSent += stats.numEventsSent; + s.stats.numEventsDropped += stats.numEventsDropped; + s.stats.numEventsEnqueued += stats.numEventsEnqueued; + s.stats.slowWriteBatch += stats.slowWriteBatch; + } + return p; + }, new Map; stats?: ClientStats }>()); + for (const [serviceGroup, state] of totals) { + // only report details if there is a discrepancy in the bookkeeping of synthtrace and the client + if ( + state.stats && + (state.stats.numEventsDropped > 0 || state.enqueued !== state.stats.numEventsSent) + ) { + this.logger.info( + ` -- ${serviceGroup} (${state.names.size} services) sent: ${state.sent}, enqueued: ${state.enqueued}` + ); + this.logger.info(` -- ${serviceGroup} (${state.names.size} services) client stats`); + this.logger.info(` -- numEvents: ${state.stats.numEvents}`); + this.logger.info(` -- numEventsSent: ${state.stats.numEventsSent}`); + this.logger.info(` -- numEventsEnqueued: ${state.stats.numEventsEnqueued}`); + this.logger.info(` -- numEventsDropped: ${state.stats.numEventsDropped}`); + this.logger.info(` -- backoffReconnectCount: ${state.stats.backoffReconnectCount}`); + this.logger.info(` -- slowWriteBatch: ${state.stats.slowWriteBatch}`); + } + } + + options?.itemStartStopCallback?.apply(this, [fields, true]); + } +} diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts index 2c8c0b9b2e799..704f51ec82519 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts @@ -62,7 +62,7 @@ export class ApmSynthtraceEsClient { async clean(dataStreams?: string[]) { return this.getWriteTargets().then(async (writeTargets) => { const indices = Object.values(writeTargets); - this.logger.info(`Attempting to clean: ${indices}`); + this.logger.info(`Attempting to clean: ${indices} + ${dataStreams ?? []}`); if (this.forceLegacyIndices) { return cleanWriteTargets({ client: this.client, @@ -132,10 +132,10 @@ export class ApmSynthtraceEsClient { this.logger.info(verifyRepository); } - async refresh() { + async refresh(dataStreams?: string[]) { const writeTargets = await this.getWriteTargets(); - const indices = Object.values(writeTargets); + const indices = Object.values(writeTargets).concat(dataStreams ?? []); this.logger.info(`Indexed all data attempting to refresh: ${indices}`); return this.client.indices.refresh({ diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/elastic_apm_http_client.d.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/elastic_apm_http_client.d.ts new file mode 100644 index 0000000000000..cf6ace8300c50 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/elastic_apm_http_client.d.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line max-classes-per-file +declare module 'elastic-apm-http-client' { + import EventEmitter from 'events'; + + class Client extends EventEmitter { + constructor(opts: ClientOptions); + + sent: number; + public setExtraMetadata(metadata: import('././intake_v2/metadata').Metadata): void; + + public sendSpan(span: import('././intake_v2/span').Span, callback: () => void): void; + public sendTransaction( + transaction: import('././intake_v2/transaction').Transaction, + callback: () => void + ): void; + public sendError(error: import('././intake_v2/error').Error, callback: () => void): void; + + public flush(opts: FlushOptions, callback: () => void): void; + public destroy(): void; + public _getStats(): ClientStats; + } + interface ClientStats { + numEvents: number; + numEventsDropped: number; + numEventsEnqueued: number; + numEventsSent: number; + slowWriteBatch: number; + backoffReconnectCount: number; + } + interface ClientOptions { + /** (required) The HTTP user agent that your module should identify itself as */ + userAgent: string; + /** The Elastic APM intake API secret token */ + secretToken?: string; + /** Elastic APM API key */ + apiKey?: string; + /** The APM Server URL (default: http://localhost:8200) */ + serverUrl: string; + maxQueueSize?: number; + bufferWindowSize?: number; + + /** (required) The APM agent name */ + agentName: string; + /** (required) The APM agent version */ + agentVersion: string; + /** The name of the service being instrumented */ + serviceName: string; + /** Unique name of the service being instrumented */ + serviceNodeName?: string; + /** The version of the service being instrumented */ + serviceVersion?: string; + /** If the service being instrumented is running a specific framework, use this config option to log its name */ + frameworkName?: string; + /** If the service being instrumented is running a specific framework, use this config option to log its version */ + frameworkVersion?: string; + /** Custom hostname (default: OS hostname) */ + hostname?: string; + /** Environment name (default: process.env.NODE_ENV || 'development') */ + environment?: string; + /** Docker container id, if not given will be parsed from /proc/self/cgroup */ + containerId?: string; + /** Kubernetes node name */ + kubernetesNodeName?: string; + /** Kubernetes namespace */ + kubernetesNamespace?: string; + /** Kubernetes pod name, if not given will be the hostname */ + kubernetesPodName?: string; + /** Kubernetes pod id, if not given will be parsed from /proc/self/cgroup */ + kubernetesPodUID?: string; + /** An object of key/value pairs to use to label all data reported (only applied when using APM Server 7.1+) */ + globalLabels?: Record; + } + class FlushOptions {} + export = Client; +} diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/error.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/error.ts new file mode 100644 index 0000000000000..a438c225939f7 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/error.ts @@ -0,0 +1,513 @@ +/* + * Copyright 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. + */ + +/** + * errorEvent represents an error or a logged error message, captured by an APM agent in a monitored service. + */ +export interface Error { + /** + * Context holds arbitrary contextual information for the event. + */ + context?: null | { + /** + * Cloud holds fields related to the cloud or infrastructure the events are coming from. + */ + cloud?: null | { + /** + * Origin contains the self-nested field groups for cloud. + */ + origin?: null | { + /** + * The cloud account or organization id used to identify different entities in a multi-tenant environment. + */ + account?: null | { + /** + * The cloud account or organization id used to identify different entities in a multi-tenant environment. + */ + id?: null | string; + [k: string]: unknown; + }; + /** + * Name of the cloud provider. + */ + provider?: null | string; + /** + * Region in which this host, resource, or service is located. + */ + region?: null | string; + /** + * The cloud service name is intended to distinguish services running on different platforms within a provider. + */ + service?: null | { + /** + * The cloud service name is intended to distinguish services running on different platforms within a provider. + */ + name?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Custom can contain additional metadata to be stored with the event. The format is unspecified and can be deeply nested objects. The information will not be indexed or searchable in Elasticsearch. + */ + custom?: null | { + [k: string]: unknown; + }; + /** + * Message holds details related to message receiving and publishing if the captured event integrates with a messaging system + */ + message?: null | { + /** + * Age of the message. If the monitored messaging framework provides a timestamp for the message, agents may use it. Otherwise, the sending agent can add a timestamp in milliseconds since the Unix epoch to the message's metadata to be retrieved by the receiving agent. If a timestamp is not available, agents should omit this field. + */ + age?: null | { + /** + * Age of the message in milliseconds. + */ + ms?: null | number; + [k: string]: unknown; + }; + /** + * Body of the received message, similar to an HTTP request body + */ + body?: null | string; + /** + * Headers received with the message, similar to HTTP request headers. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * Queue holds information about the message queue where the message is received. + */ + queue?: null | { + /** + * Name holds the name of the message queue where the message is received. + */ + name?: null | string; + [k: string]: unknown; + }; + /** + * RoutingKey holds the optional routing key of the received message as set on the queuing system, such as in RabbitMQ. + */ + routing_key?: null | string; + [k: string]: unknown; + }; + /** + * Page holds information related to the current page and page referers. It is only sent from RUM agents. + */ + page?: null | { + /** + * Referer holds the URL of the page that 'linked' to the current page. + */ + referer?: null | string; + /** + * URL of the current page + */ + url?: null | string; + [k: string]: unknown; + }; + /** + * Request describes the HTTP request information in case the event was created as a result of an HTTP request. + */ + request?: null | { + /** + * Body only contais the request bod, not the query string information. It can either be a dictionary (for standard HTTP requests) or a raw request body. + */ + body?: + | null + | string + | { + [k: string]: unknown; + }; + /** + * Cookies used by the request, parsed as key-value objects. + */ + cookies?: null | { + [k: string]: unknown; + }; + /** + * Env holds environment variable information passed to the monitored service. + */ + env?: null | { + [k: string]: unknown; + }; + /** + * Headers includes any HTTP headers sent by the requester. Cookies will be taken by headers if supplied. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * HTTPVersion holds information about the used HTTP version. + */ + http_version?: null | string; + /** + * Method holds information about the method of the HTTP request. + */ + method: string; + /** + * Socket holds information related to the recorded request, such as whether or not data were encrypted and the remote address. + */ + socket?: null | { + /** + * Encrypted indicates whether a request was sent as TLS/HTTPS request. DEPRECATED: this field will be removed in a future release. + */ + encrypted?: null | boolean; + /** + * RemoteAddress holds the network address sending the request. It should be obtained through standard APIs and not be parsed from any headers like 'Forwarded'. + */ + remote_address?: null | string; + [k: string]: unknown; + }; + /** + * URL holds information sucha as the raw URL, scheme, host and path. + */ + url?: null | { + /** + * Full, possibly agent-assembled URL of the request, e.g. https://example.com:443/search?q=elasticsearch#top. + */ + full?: null | string; + /** + * Hash of the request URL, e.g. 'top' + */ + hash?: null | string; + /** + * Hostname information of the request, e.g. 'example.com'." + */ + hostname?: null | string; + /** + * Path of the request, e.g. '/search' + */ + pathname?: null | string; + /** + * Port of the request, e.g. '443'. Can be sent as string or int. + */ + port?: null | string | number; + /** + * Protocol information for the recorded request, e.g. 'https:'. + */ + protocol?: null | string; + /** + * Raw unparsed URL of the HTTP request line, e.g https://example.com:443/search?q=elasticsearch. This URL may be absolute or relative. For more details, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2. + */ + raw?: null | string; + /** + * Search contains the query string information of the request. It is expected to have values delimited by ampersands. + */ + search?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Response describes the HTTP response information in case the event was created as a result of an HTTP request. + */ + response?: null | { + /** + * DecodedBodySize holds the size of the decoded payload. + */ + decoded_body_size?: null | number; + /** + * EncodedBodySize holds the size of the encoded payload. + */ + encoded_body_size?: null | number; + /** + * Finished indicates whether the response was finished or not. + */ + finished?: null | boolean; + /** + * Headers holds the http headers sent in the http response. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * HeadersSent indicates whether http headers were sent. + */ + headers_sent?: null | boolean; + /** + * StatusCode sent in the http response. + */ + status_code?: null | number; + /** + * TransferSize holds the total size of the payload. + */ + transfer_size?: null | number; + [k: string]: unknown; + }; + /** + * Service related information can be sent per event. Information provided here will override the more generic information retrieved from metadata, missing service fields will be retrieved from the metadata information. + */ + service?: null | { + /** + * Agent holds information about the APM agent capturing the event. + */ + agent?: null | { + /** + * EphemeralID is a free format ID used for metrics correlation by agents + */ + ephemeral_id?: null | string; + /** + * Name of the APM agent capturing information. + */ + name?: null | string; + /** + * Version of the APM agent capturing information. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Environment in which the monitored service is running, e.g. `production` or `staging`. + */ + environment?: null | string; + /** + * Framework holds information about the framework used in the monitored service. + */ + framework?: null | { + /** + * Name of the used framework + */ + name?: null | string; + /** + * Version of the used framework + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * ID holds a unique identifier for the service. + */ + id?: null | string; + /** + * Language holds information about the programming language of the monitored service. + */ + language?: null | { + /** + * Name of the used programming language + */ + name?: null | string; + /** + * Version of the used programming language + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Name of the monitored service. + */ + name?: null | string; + /** + * Node must be a unique meaningful name of the service node. + */ + node?: null | { + /** + * Name of the service node + */ + configured_name?: null | string; + [k: string]: unknown; + }; + /** + * Origin contains the self-nested field groups for service. + */ + origin?: null | { + /** + * Immutable id of the service emitting this event. + */ + id?: null | string; + /** + * Immutable name of the service emitting this event. + */ + name?: null | string; + /** + * The version of the service the data was collected from. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Runtime holds information about the language runtime running the monitored service + */ + runtime?: null | { + /** + * Name of the language runtime + */ + name?: null | string; + /** + * Version of the language runtime + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Target holds information about the outgoing service in case of an outgoing event + */ + target?: ( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) & + ( + | (( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) & + null) + | ( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) + ); + /** + * Version of the monitored service. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Tags are a flat mapping of user-defined tags. On the agent side, tags are called labels. Allowed value types are string, boolean and number values. Tags are indexed and searchable. + */ + tags?: null | { + [k: string]: null | string | boolean | number; + }; + /** + * User holds information about the correlated user for this event. If user data are provided here, all user related information from metadata is ignored, otherwise the metadata's user information will be stored with the event. + */ + user?: null | { + /** + * Domain of the logged in user + */ + domain?: null | string; + /** + * Email of the user. + */ + email?: null | string; + /** + * ID identifies the logged in user, e.g. can be the primary key of the user + */ + id?: null | string | number; + /** + * Name of the user. + */ + username?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Culprit identifies the function call which was the primary perpetrator of this event. + */ + culprit?: null | string; + /** + * Exception holds information about the original error. The information is language specific. + */ + exception?: { message: string; type: string }; + /** + * ID holds the hex encoded 128 random bits ID of the event. + */ + id: string; + /** + * Log holds additional information added when the error is logged. + */ + log?: null | { + /** + * Level represents the severity of the recorded log. + */ + level?: null | string; + /** + * LoggerName holds the name of the used logger instance. + */ + logger_name?: null | string; + /** + * Message of the logged error. In case a parameterized message is captured, Message should contain the same information, but with any placeholders being replaced. + */ + message: string; + /** + * ParamMessage should contain the same information as Message, but with placeholders where parameters were logged, e.g. 'error connecting to %s'. The string is not interpreted, allowing differnt placeholders per client languange. The information might be used to group errors together. + */ + param_message?: null | string; + /** + * Stacktrace information of the captured error. + */ + stacktrace?: null | Array< + | { + classname: string; + [k: string]: unknown; + } + | { + filename: string; + [k: string]: unknown; + } + >; + }; + /** + * ParentID holds the hex encoded 64 random bits ID of the parent transaction or span. + */ + parent_id?: null | string; + /** + * Timestamp holds the recorded time of the event, UTC based and formatted as microseconds since Unix epoch. + */ + timestamp?: null | number; + /** + * TraceID holds the hex encoded 128 random bits ID of the correlated trace. + */ + trace_id?: null | string; + /** + * Transaction holds information about the correlated transaction. + */ + transaction?: null | { + /** + * Name is the generic designation of a transaction in the scope of a single service, eg: 'GET /users/:id'. + */ + name?: null | string; + /** + * Sampled indicates whether or not the full information for a transaction is captured. If a transaction is unsampled no spans and less context information will be reported. + */ + sampled?: null | boolean; + /** + * Type expresses the correlated transaction's type as keyword that has specific relevance within the service's domain, eg: 'request', 'backgroundjob'. + */ + type?: null | string; + }; + /** + * TransactionID holds the hex encoded 64 random bits ID of the correlated transaction. + */ + transaction_id?: null | string; +} diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/metadata.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/metadata.ts new file mode 100644 index 0000000000000..551f1bc5e2b9a --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/metadata.ts @@ -0,0 +1,312 @@ +/* + * Copyright 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 Metadata { + /** + * Cloud metadata about where the monitored service is running. + */ + cloud?: null | { + /** + * Account where the monitored service is running. + */ + account?: null | { + /** + * ID of the cloud account. + */ + id?: null | string; + /** + * Name of the cloud account. + */ + name?: null | string; + [k: string]: unknown; + }; + /** + * AvailabilityZone where the monitored service is running, e.g. us-east-1a + */ + availability_zone?: null | string; + /** + * Instance on which the monitored service is running. + */ + instance?: null | { + /** + * ID of the cloud instance. + */ + id?: null | string; + /** + * Name of the cloud instance. + */ + name?: null | string; + [k: string]: unknown; + }; + /** + * Machine on which the monitored service is running. + */ + machine?: null | { + /** + * ID of the cloud machine. + */ + type?: null | string; + [k: string]: unknown; + }; + /** + * Project in which the monitored service is running. + */ + project?: null | { + /** + * ID of the cloud project. + */ + id?: null | string; + /** + * Name of the cloud project. + */ + name?: null | string; + [k: string]: unknown; + }; + /** + * Provider that is used, e.g. aws, azure, gcp, digitalocean. + */ + provider: string; + /** + * Region where the monitored service is running, e.g. us-east-1 + */ + region?: null | string; + /** + * Service that is monitored on cloud + */ + service?: null | { + /** + * Name of the cloud service, intended to distinguish services running on different platforms within a provider, eg AWS EC2 vs Lambda, GCP GCE vs App Engine, Azure VM vs App Server. + */ + name?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Labels are a flat mapping of user-defined tags. Allowed value types are string, boolean and number values. Labels are indexed and searchable. + */ + labels?: null | { + [k: string]: null | string | boolean | number; + }; + /** + * Network holds information about the network over which the monitored service is communicating. + */ + network?: null | { + connection?: null | { + type?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Process metadata about the monitored service. + */ + process?: null | { + /** + * Argv holds the command line arguments used to start this process. + */ + argv?: null | string[]; + /** + * PID holds the process ID of the service. + */ + pid: number; + /** + * Ppid holds the parent process ID of the service. + */ + ppid?: null | number; + /** + * Title is the process title. It can be the same as process name. + */ + title?: null | string; + [k: string]: unknown; + }; + /** + * Service metadata about the monitored service. + */ + service: { + /** + * Agent holds information about the APM agent capturing the event. + */ + agent: { + /** + * EphemeralID is a free format ID used for metrics correlation by agents + */ + ephemeral_id?: null | string; + /** + * Name of the APM agent capturing information. + */ + name: string; + /** + * Version of the APM agent capturing information. + */ + version: string; + [k: string]: unknown; + }; + /** + * Environment in which the monitored service is running, e.g. `production` or `staging`. + */ + environment?: null | string; + /** + * Framework holds information about the framework used in the monitored service. + */ + framework?: null | { + /** + * Name of the used framework + */ + name?: null | string; + /** + * Version of the used framework + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * ID holds a unique identifier for the running service. + */ + id?: null | string; + /** + * Language holds information about the programming language of the monitored service. + */ + language?: null | { + /** + * Name of the used programming language + */ + name: string; + /** + * Version of the used programming language + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Name of the monitored service. + */ + name: string; + /** + * Node must be a unique meaningful name of the service node. + */ + node?: null | { + /** + * Name of the service node + */ + configured_name?: null | string; + [k: string]: unknown; + }; + /** + * Runtime holds information about the language runtime running the monitored service + */ + runtime?: null | { + /** + * Name of the language runtime + */ + name: string; + /** + * Name of the language runtime + */ + version: string; + [k: string]: unknown; + }; + /** + * Version of the monitored service. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * System metadata + */ + system?: null | { + /** + * Architecture of the system the monitored service is running on. + */ + architecture?: null | string; + /** + * ConfiguredHostname is the configured name of the host the monitored service is running on. It should only be sent when configured by the user. If given, it is used as the event's hostname. + */ + configured_hostname?: null | string; + /** + * Container holds the system's container ID if available. + */ + container?: null | { + /** + * ID of the container the monitored service is running in. + */ + id?: null | string; + [k: string]: unknown; + }; + /** + * DetectedHostname is the hostname detected by the APM agent. It usually contains what the hostname command returns on the host machine. It will be used as the event's hostname if ConfiguredHostname is not present. + */ + detected_hostname?: null | string; + /** + * Deprecated: Use ConfiguredHostname and DetectedHostname instead. DeprecatedHostname is the host name of the system the service is running on. It does not distinguish between configured and detected hostname and therefore is deprecated and only used if no other hostname information is available. + */ + hostname?: null | string; + /** + * Kubernetes system information if the monitored service runs on Kubernetes. + */ + kubernetes?: null | { + /** + * Namespace of the Kubernetes resource the monitored service is run on. + */ + namespace?: null | string; + /** + * Node related information + */ + node?: null | { + /** + * Name of the Kubernetes Node + */ + name?: null | string; + [k: string]: unknown; + }; + /** + * Pod related information + */ + pod?: null | { + /** + * Name of the Kubernetes Pod + */ + name?: null | string; + /** + * UID is the system-generated string uniquely identifying the Pod. + */ + uid?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Platform name of the system platform the monitored service is running on. + */ + platform?: null | string; + [k: string]: unknown; + }; + /** + * User metadata, which can be overwritten on a per event basis. + */ + user?: null | { + /** + * Domain of the logged in user + */ + domain?: null | string; + /** + * Email of the user. + */ + email?: null | string; + /** + * ID identifies the logged in user, e.g. can be the primary key of the user + */ + id?: null | string | number; + /** + * Name of the user. + */ + username?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; +} diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/span.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/span.ts new file mode 100644 index 0000000000000..755df6dcd0d68 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/span.ts @@ -0,0 +1,447 @@ +/* + * Copyright 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 Span { + kind: 'span'; + /** + * Action holds the specific kind of event within the sub-type represented by the span (e.g. query, connect) + */ + action?: null | string; + /** + * ChildIDs holds a list of successor transactions and/or spans. + */ + child_ids?: null | string[]; + /** + * Composite holds details on a group of spans represented by a single one. + */ + composite?: null | { + /** + * A string value indicating which compression strategy was used. The valid values are `exact_match` and `same_kind`. + */ + compression_strategy: string; + /** + * Count is the number of compressed spans the composite span represents. The minimum count is 2, as a composite span represents at least two spans. + */ + count: number; + /** + * Sum is the durations of all compressed spans this composite span represents in milliseconds. + */ + sum: number; + [k: string]: unknown; + }; + /** + * Context holds arbitrary contextual information for the event. + */ + context?: null | { + /** + * Database contains contextual data for database spans + */ + db?: null | { + /** + * Instance name of the database. + */ + instance?: null | string; + /** + * Link to the database server. + */ + link?: null | string; + /** + * RowsAffected shows the number of rows affected by the statement. + */ + rows_affected?: null | number; + /** + * Statement of the recorded database event, e.g. query. + */ + statement?: null | string; + /** + * Type of the recorded database event., e.g. sql, cassandra, hbase, redis. + */ + type?: null | string; + /** + * User is the username with which the database is accessed. + */ + user?: null | string; + [k: string]: unknown; + }; + /** + * Destination contains contextual data about the destination of spans + */ + destination?: null | { + /** + * Address is the destination network address: hostname (e.g. 'localhost'), FQDN (e.g. 'elastic.co'), IPv4 (e.g. '127.0.0.1') IPv6 (e.g. '::1') + */ + address?: null | string; + /** + * Port is the destination network port (e.g. 443) + */ + port?: null | number; + /** + * Service describes the destination service + */ + service?: null | { + /** + * Name is the identifier for the destination service, e.g. 'http://elastic.co', 'elasticsearch', 'rabbitmq' ( DEPRECATED: this field will be removed in a future release + */ + name?: null | string; + /** + * Resource identifies the destination service resource being operated on e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name' DEPRECATED: this field will be removed in a future release + */ + resource: string; + /** + * Type of the destination service, e.g. db, elasticsearch. Should typically be the same as span.type. DEPRECATED: this field will be removed in a future release + */ + type?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * HTTP contains contextual information when the span concerns an HTTP request. + */ + http?: null | { + /** + * Method holds information about the method of the HTTP request. + */ + method?: null | string; + /** + * Response describes the HTTP response information in case the event was created as a result of an HTTP request. + */ + response?: null | { + /** + * DecodedBodySize holds the size of the decoded payload. + */ + decoded_body_size?: null | number; + /** + * EncodedBodySize holds the size of the encoded payload. + */ + encoded_body_size?: null | number; + /** + * Headers holds the http headers sent in the http response. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * StatusCode sent in the http response. + */ + status_code?: null | number; + /** + * TransferSize holds the total size of the payload. + */ + transfer_size?: null | number; + [k: string]: unknown; + }; + /** + * Deprecated: Use Response.StatusCode instead. StatusCode sent in the http response. + */ + status_code?: null | number; + /** + * URL is the raw url of the correlating HTTP request. + */ + url?: null | string; + [k: string]: unknown; + }; + /** + * Message holds details related to message receiving and publishing if the captured event integrates with a messaging system + */ + message?: null | { + /** + * Age of the message. If the monitored messaging framework provides a timestamp for the message, agents may use it. Otherwise, the sending agent can add a timestamp in milliseconds since the Unix epoch to the message's metadata to be retrieved by the receiving agent. If a timestamp is not available, agents should omit this field. + */ + age?: null | { + /** + * Age of the message in milliseconds. + */ + ms?: null | number; + [k: string]: unknown; + }; + /** + * Body of the received message, similar to an HTTP request body + */ + body?: null | string; + /** + * Headers received with the message, similar to HTTP request headers. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * Queue holds information about the message queue where the message is received. + */ + queue?: null | { + /** + * Name holds the name of the message queue where the message is received. + */ + name?: null | string; + [k: string]: unknown; + }; + /** + * RoutingKey holds the optional routing key of the received message as set on the queuing system, such as in RabbitMQ. + */ + routing_key?: null | string; + [k: string]: unknown; + }; + /** + * Service related information can be sent per span. Information provided here will override the more generic information retrieved from metadata, missing service fields will be retrieved from the metadata information. + */ + service?: null | { + /** + * Agent holds information about the APM agent capturing the event. + */ + agent?: null | { + /** + * EphemeralID is a free format ID used for metrics correlation by agents + */ + ephemeral_id?: null | string; + /** + * Name of the APM agent capturing information. + */ + name?: null | string; + /** + * Version of the APM agent capturing information. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Environment in which the monitored service is running, e.g. `production` or `staging`. + */ + environment?: null | string; + /** + * Framework holds information about the framework used in the monitored service. + */ + framework?: null | { + /** + * Name of the used framework + */ + name?: null | string; + /** + * Version of the used framework + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * ID holds a unique identifier for the service. + */ + id?: null | string; + /** + * Language holds information about the programming language of the monitored service. + */ + language?: null | { + /** + * Name of the used programming language + */ + name?: null | string; + /** + * Version of the used programming language + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Name of the monitored service. + */ + name?: null | string; + /** + * Node must be a unique meaningful name of the service node. + */ + node?: null | { + /** + * Name of the service node + */ + configured_name?: null | string; + [k: string]: unknown; + }; + /** + * Origin contains the self-nested field groups for service. + */ + origin?: null | { + /** + * Immutable id of the service emitting this event. + */ + id?: null | string; + /** + * Immutable name of the service emitting this event. + */ + name?: null | string; + /** + * The version of the service the data was collected from. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Runtime holds information about the language runtime running the monitored service + */ + runtime?: null | { + /** + * Name of the language runtime + */ + name?: null | string; + /** + * Version of the language runtime + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Target holds information about the outgoing service in case of an outgoing event + */ + target?: ( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) & + ( + | (( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) & + null) + | ( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) + ); + /** + * Version of the monitored service. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Tags are a flat mapping of user-defined tags. On the agent side, tags are called labels. Allowed value types are string, boolean and number values. Tags are indexed and searchable. + */ + tags?: null | { + [k: string]: null | string | boolean | number; + }; + [k: string]: unknown; + }; + /** + * Duration of the span in milliseconds. When the span is a composite one, duration is the gross duration, including "whitespace" in between spans. + */ + duration: number; + /** + * ID holds the hex encoded 64 random bits ID of the event. + */ + id: string; + /** + * Links holds links to other spans, potentially in other traces. + */ + links?: null | Array<{ + /** + * SpanID holds the ID of the linked span. + */ + span_id: string; + /** + * TraceID holds the ID of the linked span's trace. + */ + trace_id: string; + [k: string]: unknown; + }>; + /** + * Name is the generic designation of a span in the scope of a transaction. + */ + name: string; + /** + * OTel contains unmapped OpenTelemetry attributes. + */ + otel?: null | { + /** + * Attributes hold the unmapped OpenTelemetry attributes. + */ + attributes?: null | { + [k: string]: unknown; + }; + /** + * SpanKind holds the incoming OpenTelemetry span kind. + */ + span_kind?: null | string; + [k: string]: unknown; + }; + /** + * Outcome of the span: success, failure, or unknown. Outcome may be one of a limited set of permitted values describing the success or failure of the span. It can be used for calculating error rates for outgoing requests. + */ + outcome?: 'success' | 'failure' | 'unknown' | null; + /** + * ParentID holds the hex encoded 64 random bits ID of the parent transaction or span. + */ + parent_id: string; + /** + * SampleRate applied to the monitored service at the time where this span was recorded. + */ + sample_rate?: null | number; + /** + * Stacktrace connected to this span event. + */ + stacktrace?: null | Array< + | { + classname: string; + [k: string]: unknown; + } + | { + filename: string; + [k: string]: unknown; + } + >; + /** + * Start is the offset relative to the transaction's timestamp identifying the start of the span, in milliseconds. + */ + start?: null | number; + /** + * Subtype is a further sub-division of the type (e.g. postgresql, elasticsearch) + */ + subtype?: null | string; + /** + * Sync indicates whether the span was executed synchronously or asynchronously. + */ + sync?: null | boolean; + /** + * Timestamp holds the recorded time of the event, UTC based and formatted as microseconds since Unix epoch + */ + timestamp?: null | number; + /** + * TraceID holds the hex encoded 128 random bits ID of the correlated trace. + */ + trace_id: string; + /** + * TransactionID holds the hex encoded 64 random bits ID of the correlated transaction. + */ + transaction_id?: null | string; + /** + * Type holds the span's type, and can have specific keywords within the service's domain (eg: 'request', 'backgroundjob', etc) + */ + type: string; + [k: string]: unknown; +} diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/transaction.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/transaction.ts new file mode 100644 index 0000000000000..69588c95332b2 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/intake_v2/transaction.ts @@ -0,0 +1,661 @@ +/* + * Copyright 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 Transaction { + kind: 'transaction'; + /** + * Context holds arbitrary contextual information for the event. + */ + context?: null | { + /** + * Cloud holds fields related to the cloud or infrastructure the events are coming from. + */ + cloud?: null | { + /** + * Origin contains the self-nested field groups for cloud. + */ + origin?: null | { + /** + * The cloud account or organization id used to identify different entities in a multi-tenant environment. + */ + account?: null | { + /** + * The cloud account or organization id used to identify different entities in a multi-tenant environment. + */ + id?: null | string; + [k: string]: unknown; + }; + /** + * Name of the cloud provider. + */ + provider?: null | string; + /** + * Region in which this host, resource, or service is located. + */ + region?: null | string; + /** + * The cloud service name is intended to distinguish services running on different platforms within a provider. + */ + service?: null | { + /** + * The cloud service name is intended to distinguish services running on different platforms within a provider. + */ + name?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Custom can contain additional metadata to be stored with the event. The format is unspecified and can be deeply nested objects. The information will not be indexed or searchable in Elasticsearch. + */ + custom?: null | { + [k: string]: unknown; + }; + /** + * Message holds details related to message receiving and publishing if the captured event integrates with a messaging system + */ + message?: null | { + /** + * Age of the message. If the monitored messaging framework provides a timestamp for the message, agents may use it. Otherwise, the sending agent can add a timestamp in milliseconds since the Unix epoch to the message's metadata to be retrieved by the receiving agent. If a timestamp is not available, agents should omit this field. + */ + age?: null | { + /** + * Age of the message in milliseconds. + */ + ms?: null | number; + [k: string]: unknown; + }; + /** + * Body of the received message, similar to an HTTP request body + */ + body?: null | string; + /** + * Headers received with the message, similar to HTTP request headers. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * Queue holds information about the message queue where the message is received. + */ + queue?: null | { + /** + * Name holds the name of the message queue where the message is received. + */ + name?: null | string; + [k: string]: unknown; + }; + /** + * RoutingKey holds the optional routing key of the received message as set on the queuing system, such as in RabbitMQ. + */ + routing_key?: null | string; + [k: string]: unknown; + }; + /** + * Page holds information related to the current page and page referers. It is only sent from RUM agents. + */ + page?: null | { + /** + * Referer holds the URL of the page that 'linked' to the current page. + */ + referer?: null | string; + /** + * URL of the current page + */ + url?: null | string; + [k: string]: unknown; + }; + /** + * Request describes the HTTP request information in case the event was created as a result of an HTTP request. + */ + request?: null | { + /** + * Body only contais the request bod, not the query string information. It can either be a dictionary (for standard HTTP requests) or a raw request body. + */ + body?: + | null + | string + | { + [k: string]: unknown; + }; + /** + * Cookies used by the request, parsed as key-value objects. + */ + cookies?: null | { + [k: string]: unknown; + }; + /** + * Env holds environment variable information passed to the monitored service. + */ + env?: null | { + [k: string]: unknown; + }; + /** + * Headers includes any HTTP headers sent by the requester. Cookies will be taken by headers if supplied. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * HTTPVersion holds information about the used HTTP version. + */ + http_version?: null | string; + /** + * Method holds information about the method of the HTTP request. + */ + method: string; + /** + * Socket holds information related to the recorded request, such as whether or not data were encrypted and the remote address. + */ + socket?: null | { + /** + * Encrypted indicates whether a request was sent as TLS/HTTPS request. DEPRECATED: this field will be removed in a future release. + */ + encrypted?: null | boolean; + /** + * RemoteAddress holds the network address sending the request. It should be obtained through standard APIs and not be parsed from any headers like 'Forwarded'. + */ + remote_address?: null | string; + [k: string]: unknown; + }; + /** + * URL holds information sucha as the raw URL, scheme, host and path. + */ + url?: null | { + /** + * Full, possibly agent-assembled URL of the request, e.g. https://example.com:443/search?q=elasticsearch#top. + */ + full?: null | string; + /** + * Hash of the request URL, e.g. 'top' + */ + hash?: null | string; + /** + * Hostname information of the request, e.g. 'example.com'." + */ + hostname?: null | string; + /** + * Path of the request, e.g. '/search' + */ + pathname?: null | string; + /** + * Port of the request, e.g. '443'. Can be sent as string or int. + */ + port?: null | string | number; + /** + * Protocol information for the recorded request, e.g. 'https:'. + */ + protocol?: null | string; + /** + * Raw unparsed URL of the HTTP request line, e.g https://example.com:443/search?q=elasticsearch. This URL may be absolute or relative. For more details, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2. + */ + raw?: null | string; + /** + * Search contains the query string information of the request. It is expected to have values delimited by ampersands. + */ + search?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Response describes the HTTP response information in case the event was created as a result of an HTTP request. + */ + response?: null | { + /** + * DecodedBodySize holds the size of the decoded payload. + */ + decoded_body_size?: null | number; + /** + * EncodedBodySize holds the size of the encoded payload. + */ + encoded_body_size?: null | number; + /** + * Finished indicates whether the response was finished or not. + */ + finished?: null | boolean; + /** + * Headers holds the http headers sent in the http response. + */ + headers?: null | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[.*]*$". + */ + [k: string]: null | string[] | string; + }; + /** + * HeadersSent indicates whether http headers were sent. + */ + headers_sent?: null | boolean; + /** + * StatusCode sent in the http response. + */ + status_code?: null | number; + /** + * TransferSize holds the total size of the payload. + */ + transfer_size?: null | number; + [k: string]: unknown; + }; + /** + * Service related information can be sent per event. Information provided here will override the more generic information retrieved from metadata, missing service fields will be retrieved from the metadata information. + */ + service?: null | { + /** + * Agent holds information about the APM agent capturing the event. + */ + agent?: null | { + /** + * EphemeralID is a free format ID used for metrics correlation by agents + */ + ephemeral_id?: null | string; + /** + * Name of the APM agent capturing information. + */ + name?: null | string; + /** + * Version of the APM agent capturing information. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Environment in which the monitored service is running, e.g. `production` or `staging`. + */ + environment?: null | string; + /** + * Framework holds information about the framework used in the monitored service. + */ + framework?: null | { + /** + * Name of the used framework + */ + name?: null | string; + /** + * Version of the used framework + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * ID holds a unique identifier for the service. + */ + id?: null | string; + /** + * Language holds information about the programming language of the monitored service. + */ + language?: null | { + /** + * Name of the used programming language + */ + name?: null | string; + /** + * Version of the used programming language + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Name of the monitored service. + */ + name?: null | string; + /** + * Node must be a unique meaningful name of the service node. + */ + node?: null | { + /** + * Name of the service node + */ + configured_name?: null | string; + [k: string]: unknown; + }; + /** + * Origin contains the self-nested field groups for service. + */ + origin?: null | { + /** + * Immutable id of the service emitting this event. + */ + id?: null | string; + /** + * Immutable name of the service emitting this event. + */ + name?: null | string; + /** + * The version of the service the data was collected from. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Runtime holds information about the language runtime running the monitored service + */ + runtime?: null | { + /** + * Name of the language runtime + */ + name?: null | string; + /** + * Version of the language runtime + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Target holds information about the outgoing service in case of an outgoing event + */ + target?: ( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) & + ( + | (( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) & + null) + | ( + | { + type: string; + [k: string]: unknown; + } + | { + name: string; + [k: string]: unknown; + } + ) + ); + /** + * Version of the monitored service. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * Tags are a flat mapping of user-defined tags. On the agent side, tags are called labels. Allowed value types are string, boolean and number values. Tags are indexed and searchable. + */ + tags?: null | { + [k: string]: null | string | boolean | number; + }; + /** + * User holds information about the correlated user for this event. If user data are provided here, all user related information from metadata is ignored, otherwise the metadata's user information will be stored with the event. + */ + user?: null | { + /** + * Domain of the logged in user + */ + domain?: null | string; + /** + * Email of the user. + */ + email?: null | string; + /** + * ID identifies the logged in user, e.g. can be the primary key of the user + */ + id?: null | string | number; + /** + * Name of the user. + */ + username?: null | string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * DroppedSpanStats holds information about spans that were dropped (for example due to transaction_max_spans or exit_span_min_duration). + */ + dropped_spans_stats?: null | Array<{ + /** + * DestinationServiceResource identifies the destination service resource being operated on. e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name'. + */ + destination_service_resource?: null | string; + /** + * Duration holds duration aggregations about the dropped span. + */ + duration?: null | { + /** + * Count holds the number of times the dropped span happened. + */ + count?: null | number; + /** + * Sum holds dimensions about the dropped span's duration. + */ + sum?: null | { + /** + * Us represents the summation of the span duration. + */ + us?: null | number; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + /** + * Outcome of the span: success, failure, or unknown. Outcome may be one of a limited set of permitted values describing the success or failure of the span. It can be used for calculating error rates for outgoing requests. + */ + outcome?: 'success' | 'failure' | 'unknown' | null; + /** + * ServiceTargetName identifies the instance name of the target service being operated on + */ + service_target_name?: null | string; + /** + * ServiceTargetType identifies the type of the target service being operated on e.g. 'oracle', 'rabbitmq' + */ + service_target_type?: null | string; + [k: string]: unknown; + }>; + /** + * Duration how long the transaction took to complete, in milliseconds with 3 decimal points. + */ + duration: number; + /** + * UserExperience holds metrics for measuring real user experience. This information is only sent by RUM agents. + */ + experience?: null | { + /** + * CumulativeLayoutShift holds the Cumulative Layout Shift (CLS) metric value, or a negative value if CLS is unknown. See https://web.dev/cls/ + */ + cls?: null | number; + /** + * FirstInputDelay holds the First Input Delay (FID) metric value, or a negative value if FID is unknown. See https://web.dev/fid/ + */ + fid?: null | number; + /** + * Longtask holds longtask duration/count metrics. + */ + longtask?: null | { + /** + * Count is the total number of of longtasks. + */ + count: number; + /** + * Max longtask duration + */ + max: number; + /** + * Sum of longtask durations + */ + sum: number; + [k: string]: unknown; + }; + /** + * TotalBlockingTime holds the Total Blocking Time (TBT) metric value, or a negative value if TBT is unknown. See https://web.dev/tbt/ + */ + tbt?: null | number; + [k: string]: unknown; + }; + /** + * FAAS holds fields related to Function as a Service events. + */ + faas?: null | { + /** + * Indicates whether a function invocation was a cold start or not. + */ + coldstart?: null | boolean; + /** + * The request id of the function invocation. + */ + execution?: null | string; + /** + * A unique identifier of the invoked serverless function. + */ + id?: null | string; + /** + * The lambda function name. + */ + name?: null | string; + /** + * Trigger attributes. + */ + trigger?: null | { + /** + * The id of the origin trigger request. + */ + request_id?: null | string; + /** + * The trigger type. + */ + type?: null | string; + [k: string]: unknown; + }; + /** + * The lambda function version. + */ + version?: null | string; + [k: string]: unknown; + }; + /** + * ID holds the hex encoded 64 random bits ID of the event. + */ + id: string; + /** + * Links holds links to other spans, potentially in other traces. + */ + links?: null | Array<{ + /** + * SpanID holds the ID of the linked span. + */ + span_id: string; + /** + * TraceID holds the ID of the linked span's trace. + */ + trace_id: string; + [k: string]: unknown; + }>; + /** + * Marks capture the timing of a significant event during the lifetime of a transaction. Marks are organized into groups and can be set by the user or the agent. Marks are only reported by RUM agents. + */ + marks?: null | { + [k: string]: null | { + [k: string]: null | number; + }; + }; + /** + * Name is the generic designation of a transaction in the scope of a single service, eg: 'GET /users/:id'. + */ + name?: null | string; + /** + * OTel contains unmapped OpenTelemetry attributes. + */ + otel?: null | { + /** + * Attributes hold the unmapped OpenTelemetry attributes. + */ + attributes?: null | { + [k: string]: unknown; + }; + /** + * SpanKind holds the incoming OpenTelemetry span kind. + */ + span_kind?: null | string; + [k: string]: unknown; + }; + /** + * Outcome of the transaction with a limited set of permitted values, describing the success or failure of the transaction from the service's perspective. It is used for calculating error rates for incoming requests. Permitted values: success, failure, unknown. + */ + outcome?: 'success' | 'failure' | 'unknown' | null; + /** + * ParentID holds the hex encoded 64 random bits ID of the parent transaction or span. + */ + parent_id?: null | string; + /** + * Result of the transaction. For HTTP-related transactions, this should be the status code formatted like 'HTTP 2xx'. + */ + result?: null | string; + /** + * SampleRate applied to the monitored service at the time where this transaction was recorded. Allowed values are [0..1]. A SampleRate <1 indicates that not all spans are recorded. + */ + sample_rate?: null | number; + /** + * Sampled indicates whether or not the full information for a transaction is captured. If a transaction is unsampled no spans and less context information will be reported. + */ + sampled?: null | boolean; + /** + * Session holds optional transaction session information for RUM. + */ + session?: null | { + /** + * ID holds a session ID for grouping a set of related transactions. + */ + id: string; + /** + * Sequence holds an optional sequence number for a transaction within a session. It is not meaningful to compare sequences across two different sessions. + */ + sequence?: null | number; + [k: string]: unknown; + }; + /** + * SpanCount counts correlated spans. + */ + span_count: { + /** + * Dropped is the number of correlated spans that have been dropped by the APM agent recording the transaction. + */ + dropped?: null | number; + /** + * Started is the number of correlated spans that are recorded. + */ + started: number; + [k: string]: unknown; + }; + /** + * Timestamp holds the recorded time of the event, UTC based and formatted as microseconds since Unix epoch + */ + timestamp?: null | number; + /** + * TraceID holds the hex encoded 128 random bits ID of the correlated trace. + */ + trace_id: string; + /** + * Type expresses the transaction's type as keyword that has specific relevance within the service's domain, eg: 'request', 'backgroundjob'. + */ + type: string; + [k: string]: unknown; +} diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/instance.ts b/packages/kbn-apm-synthtrace/src/lib/apm/instance.ts index c89fda7f576fb..d212c1f2cead0 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/instance.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/instance.ts @@ -22,9 +22,10 @@ export class Instance extends Entity { }); } - span(spanName: string, spanType: string, spanSubtype?: string) { + span(spanName: string, spanType: string, spanSubtype?: string, apmFields?: ApmFields) { return new Span({ ...this.fields, + ...apmFields, 'span.name': spanName, 'span.type': spanType, 'span.subtype': spanSubtype, diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/span.ts b/packages/kbn-apm-synthtrace/src/lib/apm/span.ts index 91cbacadf59cc..388e65385e7dd 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/span.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/span.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import url from 'url'; import { BaseSpan } from './base_span'; import { generateShortId } from '../utils/generate_id'; import { ApmFields } from './apm_fields'; @@ -39,3 +40,56 @@ export class Span extends BaseSpan { return this; } } + +export function httpExitSpan({ + spanName, + destinationUrl, +}: { + spanName: string; + destinationUrl: string; +}): [string, string, string, ApmFields] { + // origin: 'http://opbeans-go:3000', + // host: 'opbeans-go:3000', + // hostname: 'opbeans-go', + // port: '3000', + const destination = new url.URL(destinationUrl); + + const spanType = 'external'; + const spanSubType = 'http'; + + return [ + spanName, + spanType, + spanSubType, + { + 'destination.address': destination.hostname, + 'destination.port': parseInt(destination.port, 10), + 'service.target.name': destination.host, + 'span.destination.service.name': destination.origin, + 'span.destination.service.resource': destination.host, + 'span.destination.service.type': 'external', + }, + ]; +} + +export function dbExitSpan({ + spanName, + spanSubType, +}: { + spanName: string; + spanSubType?: string; +}): [string, string, string | undefined, ApmFields] { + const spanType = 'db'; + + return [ + spanName, + spanType, + spanSubType, + { + 'service.target.type': spanSubType, + 'span.destination.service.name': spanSubType, + 'span.destination.service.resource': spanSubType, + 'span.destination.service.type': spanType, + }, + ]; +} diff --git a/packages/kbn-apm-synthtrace/src/lib/entity_generator.ts b/packages/kbn-apm-synthtrace/src/lib/entity_generator.ts index 67ae790a46c20..08263ab152432 100644 --- a/packages/kbn-apm-synthtrace/src/lib/entity_generator.ts +++ b/packages/kbn-apm-synthtrace/src/lib/entity_generator.ts @@ -35,7 +35,7 @@ export class EntityGenerator implements EntityIterable { }; const peekedNumberOfEvents = peek.done ? 0 : peek.value.serialize().length; - this._ratePerMinute = interval.ratePerMinute() * peekedNumberOfEvents; + this._ratePerMinute = interval.estimatedRatePerMinute() * peekedNumberOfEvents; } private readonly _order: 'desc' | 'asc'; @@ -52,7 +52,7 @@ export class EntityGenerator implements EntityIterable { } private readonly _ratePerMinute: number; - ratePerMinute() { + estimatedRatePerMinute() { return this._ratePerMinute; } diff --git a/packages/kbn-apm-synthtrace/src/lib/entity_iterable.ts b/packages/kbn-apm-synthtrace/src/lib/entity_iterable.ts index 2bc7a52e84d8c..77b18e57345db 100644 --- a/packages/kbn-apm-synthtrace/src/lib/entity_iterable.ts +++ b/packages/kbn-apm-synthtrace/src/lib/entity_iterable.ts @@ -15,7 +15,7 @@ export interface EntityIterable AsyncIterable { order(): 'desc' | 'asc'; - ratePerMinute(): number; + estimatedRatePerMinute(): number; toArray(): ApmFields[]; @@ -40,7 +40,7 @@ export class EntityArrayIterable } private readonly _ratePerMinute: number; - ratePerMinute() { + estimatedRatePerMinute() { return this._ratePerMinute; } diff --git a/packages/kbn-apm-synthtrace/src/lib/entity_streams.ts b/packages/kbn-apm-synthtrace/src/lib/entity_streams.ts index 4d5e8311c0e7e..c706ef2065f28 100644 --- a/packages/kbn-apm-synthtrace/src/lib/entity_streams.ts +++ b/packages/kbn-apm-synthtrace/src/lib/entity_streams.ts @@ -15,7 +15,9 @@ export class EntityStreams implements EntityIterable { if (orders.size > 1) throw Error('Can only combine intervals with the same order()'); this._order = orders.has('asc') ? 'asc' : 'desc'; - this._ratePerMinute = dataGenerators.map((d) => d.ratePerMinute()).reduce((a, b) => a + b, 0); + this._ratePerMinute = dataGenerators + .map((d) => d.estimatedRatePerMinute()) + .reduce((a, b) => a + b, 0); } private readonly _order: 'desc' | 'asc'; @@ -24,7 +26,7 @@ export class EntityStreams implements EntityIterable { } private readonly _ratePerMinute: number; - ratePerMinute() { + estimatedRatePerMinute() { return this._ratePerMinute; } diff --git a/packages/kbn-apm-synthtrace/src/lib/interval.ts b/packages/kbn-apm-synthtrace/src/lib/interval.ts index 48d15a1a4d53f..a3fef71b79fda 100644 --- a/packages/kbn-apm-synthtrace/src/lib/interval.ts +++ b/packages/kbn-apm-synthtrace/src/lib/interval.ts @@ -11,12 +11,18 @@ import { EntityIterable } from './entity_iterable'; import { EntityGenerator } from './entity_generator'; import { Serializable } from './serializable'; -export function parseInterval(interval: string): [number, unitOfTime.DurationConstructor] { +export function parseInterval(interval: string): { + intervalAmount: number; + intervalUnit: unitOfTime.DurationConstructor; +} { const args = interval.match(/(\d+)(s|m|h|d)/); if (!args || args.length < 3) { throw new Error('Failed to parse interval'); } - return [Number(args[1]), args[2] as any]; + return { + intervalAmount: Number(args[1]), + intervalUnit: args[2] as unitOfTime.DurationConstructor, + }; } export interface IntervalOptions { @@ -31,9 +37,9 @@ export interface IntervalOptions { export class Interval implements Iterable { constructor(public readonly options: IntervalOptions) { - const parsed = parseInterval(options.interval); - this.intervalAmount = parsed[0]; - this.intervalUnit = parsed[1]; + const { intervalAmount, intervalUnit } = parseInterval(options.interval); + this.intervalAmount = intervalAmount; + this.intervalUnit = intervalUnit; this.from = this.options.from; this.to = this.options.to; } @@ -66,7 +72,7 @@ export class Interval implements Iterable { return new Interval({ ...this.options, intervalUpper, rateUpper }); } - ratePerMinute(): number { + estimatedRatePerMinute(): number { const rate = this.options.rateUpper ? Math.max(1, this.options.rateUpper) : this.options.yieldRate ?? 1; diff --git a/packages/kbn-apm-synthtrace/src/lib/stream_processor.ts b/packages/kbn-apm-synthtrace/src/lib/stream_processor.ts index a6f8f923b3714..0d7d0ff5dfa51 100644 --- a/packages/kbn-apm-synthtrace/src/lib/stream_processor.ts +++ b/packages/kbn-apm-synthtrace/src/lib/stream_processor.ts @@ -45,9 +45,11 @@ export class StreamProcessor { private readonly streamAggregators: Array>; constructor(private readonly options: StreamProcessorOptions) { - [this.intervalAmount, this.intervalUnit] = this.options.flushInterval + const { intervalAmount, intervalUnit } = this.options.flushInterval ? parseInterval(this.options.flushInterval) : parseInterval('1m'); + this.intervalAmount = intervalAmount; + this.intervalUnit = intervalUnit; this.name = this.options?.name ?? 'StreamProcessor'; this.version = this.options.version ?? '8.0.0'; this.versionMajor = Number.parseInt(this.version.split('.')[0], 10); @@ -56,8 +58,8 @@ export class StreamProcessor { } private readonly intervalAmount: number; private readonly intervalUnit: any; - private readonly name: string; - private readonly version: string; + public readonly name: string; + public readonly version: string; private readonly versionMajor: number; // TODO move away from chunking and feed this data one by one to processors @@ -178,6 +180,7 @@ export class StreamProcessor { private static enrich(document: ApmFields, version: string, versionMajor: number): ApmFields { // see https://github.com/elastic/apm-server/issues/7088 can not be provided as flat key/values document.observer = { + type: 'synthtrace', version: version ?? '8.2.0', version_major: versionMajor, }; diff --git a/packages/kbn-apm-synthtrace/src/lib/timerange.ts b/packages/kbn-apm-synthtrace/src/lib/timerange.ts index 41532157b3b91..5658159fec8b9 100644 --- a/packages/kbn-apm-synthtrace/src/lib/timerange.ts +++ b/packages/kbn-apm-synthtrace/src/lib/timerange.ts @@ -14,6 +14,22 @@ export class Timerange { interval(interval: string) { return new Interval({ from: this.from, to: this.to, interval }); } + + ratePerMinute(rateInTpm: number) { + const intervalPerSecond = Math.max(1, 60 / rateInTpm); + + // rate per second + let interval = `${intervalPerSecond}s`; + let rate = (rateInTpm / 60) * intervalPerSecond; + + // rate per minute + if (!Number.isInteger(rate) || !Number.isInteger(intervalPerSecond)) { + interval = '1m'; + rate = rate * 60; + } + + return new Interval({ from: this.from, to: this.to, interval, yieldRate: rate }); + } } export function timerange(from: Date | number, to: Date | number) { diff --git a/packages/kbn-apm-synthtrace/src/lib/utils/merge_iterable.ts b/packages/kbn-apm-synthtrace/src/lib/utils/merge_iterable.ts index 1b6fe82e7bd46..ab48a99bea5ae 100644 --- a/packages/kbn-apm-synthtrace/src/lib/utils/merge_iterable.ts +++ b/packages/kbn-apm-synthtrace/src/lib/utils/merge_iterable.ts @@ -17,7 +17,7 @@ export function merge( if (iterables.length === 1) return iterables[0]; const iterators = iterables.map<{ it: Iterator; weight: number }>((i) => { - return { it: i[Symbol.iterator](), weight: Math.max(1, i.ratePerMinute()) }; + return { it: i[Symbol.iterator](), weight: Math.max(1, i.estimatedRatePerMinute()) }; }); let done = false; const myIterable: Iterable = { diff --git a/packages/kbn-apm-synthtrace/src/scenarios/aws_lambda.ts b/packages/kbn-apm-synthtrace/src/scenarios/aws_lambda.ts index 95879d6912d7a..fa04d6e4f6465 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/aws_lambda.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/aws_lambda.ts @@ -21,7 +21,7 @@ const scenario: Scenario = async (runOptions: RunOptions) => { return { generate: ({ from, to }) => { const range = timerange(from, to); - const timestamps = range.interval('1s').rate(3); + const timestamps = range.ratePerMinute(180); const instance = apm.service('lambda-python', ENVIRONMENT, 'python').instance('instance'); diff --git a/packages/kbn-apm-synthtrace/src/scenarios/distributed_trace.ts b/packages/kbn-apm-synthtrace/src/scenarios/distributed_trace.ts new file mode 100644 index 0000000000000..a87cbfe5ab4d3 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/scenarios/distributed_trace.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 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 { apm, timerange } from '../..'; +import { ApmFields } from '../lib/apm/apm_fields'; +import { Scenario } from '../cli/scenario'; + +import { RunOptions } from '../cli/utils/parse_run_cli_flags'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; +import { httpExitSpan } from '../lib/apm/span'; + +const ENVIRONMENT = getSynthtraceEnvironment(__filename); + +const scenario: Scenario = async (runOptions: RunOptions) => { + return { + generate: ({ from, to }) => { + const range = timerange(from, to); + const transactionName = '240rpm/75% 1000ms'; + const successfulTimestamps = range.interval('1s').rate(3); + + const opbeansRum = apm.service('opbeans-rum', ENVIRONMENT, 'rum-js').instance('my-instance'); + const opbeansNode = apm + .service('opbeans-node', ENVIRONMENT, 'nodejs') + .instance('my-instance'); + const opbeansGo = apm.service('opbeans-go', ENVIRONMENT, 'go').instance('my-instance'); + + const traces = successfulTimestamps.generator((timestamp) => { + // opbeans-rum + return opbeansRum + .transaction(transactionName) + .duration(400) + .timestamp(timestamp) + .children( + // opbeans-rum -> opbeans-node + opbeansRum + .span( + ...httpExitSpan({ + spanName: 'GET /api/products/top', + destinationUrl: 'http://opbeans-node:3000', + }) + ) + .duration(300) + .timestamp(timestamp) + + .children( + // opbeans-node + opbeansNode + .transaction('Initial transaction in opbeans-node') + .duration(300) + .timestamp(timestamp) + .children( + opbeansNode + // opbeans-node -> opbeans-go + .span( + ...httpExitSpan({ + spanName: 'GET opbeans-go:3000', + destinationUrl: 'http://opbeans-go:3000', + }) + ) + .timestamp(timestamp) + .duration(400) + + .children( + // opbeans-go + opbeansGo + + .transaction('Initial transaction in opbeans-go') + .timestamp(timestamp) + .duration(200) + .children( + opbeansGo + .span('custom_operation', 'custom') + .timestamp(timestamp) + .duration(100) + .success() + ) + ) + ) + ) + ); + }); + + return traces; + }, + }; +}; + +export default scenario; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts b/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts new file mode 100644 index 0000000000000..41b21df2e83e1 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { random } from 'lodash'; +import { apm, timerange } from '../..'; +import { ApmFields } from '../lib/apm/apm_fields'; +import { Instance } from '../lib/apm/instance'; +import { Scenario } from '../cli/scenario'; +import { getLogger } from '../cli/utils/get_common_services'; +import { RunOptions } from '../cli/utils/parse_run_cli_flags'; + +const scenario: Scenario = async (runOptions: RunOptions) => { + const logger = getLogger(runOptions); + + const languages = ['go', 'dotnet', 'java', 'python']; + const services = ['web', 'order-processing', 'api-backend']; + + return { + generate: ({ from, to }) => { + const range = timerange(from, to); + + const successfulTimestamps = range.interval('1s').randomize(100, 180); + + const instances = services.map((service, index) => + apm + .service( + `${service}-${languages[index % languages.length]}`, + 'production', + languages[index % languages.length] + ) + .instance(`instance-${index}`) + ); + const entities = [ + 'order', + 'book', + 'product', + 'baskets', + 'user', + 'exporter', + 'set', + 'profile', + ]; + const routes = (e: string) => { + return [ + `HEAD /${e}/{id}`, + `GET /${e}/{id}`, + `PUT /${e}s`, + `POST /${e}s`, + `DELETE /${e}/{id}`, + `GET /${e}s`, + ]; + }; + const urls = entities.flatMap(routes); + + const instanceSpans = (instance: Instance, url: string, index: number) => { + const successfulTraceEvents = successfulTimestamps.generator((timestamp) => { + const mod = (index % 4) + 1; + const randomHigh = random(100, mod * 1000, false); + const randomLow = random(10, randomHigh / 10 + mod * 3, false); + const duration = random(randomLow, randomHigh, false); + const childDuration = random(1, duration); + const remainderDuration = duration - childDuration; + const generateError = index % random(mod, 9) === 0; + const generateChildError = index % random(mod, 9) === 0; + const span = instance + .transaction(url) + .timestamp(timestamp) + .duration(duration) + .children( + instance + .span('GET apm-*/_search', 'db', 'elasticsearch') + .duration(childDuration) + .destination('elasticsearch') + .timestamp(timestamp) + .outcome(generateError && generateChildError ? 'failure' : 'success'), + instance + .span('custom_operation', 'custom') + .duration(remainderDuration) + .success() + .timestamp(timestamp + childDuration) + ); + return !generateError + ? span.success() + : span + .failure() + .errors(instance.error(`No handler for ${url}`).timestamp(timestamp + 50)); + }); + + return successfulTraceEvents; + }; + + return instances + .flatMap((instance) => urls.map((url) => ({ instance, url }))) + .map(({ instance, url }, index) => + logger.perf('generating_apm_events', () => instanceSpans(instance, url, index)) + ) + .reduce((p, c) => p.merge(c)); + }, + }; +}; + +export default scenario; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/low_throughput.ts b/packages/kbn-apm-synthtrace/src/scenarios/low_throughput.ts index ad94ba7eb1e7c..d842a0650b423 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/low_throughput.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/low_throughput.ts @@ -20,36 +20,35 @@ const ENVIRONMENT = getSynthtraceEnvironment(__filename); const scenario: Scenario = async (runOptions: RunOptions) => { const logger = getLogger(runOptions); - const numServices = 3; const languages = ['go', 'dotnet', 'java', 'python']; - const services = ['web', 'order-processing', 'api-backend', 'proxy']; + const services = ['web', 'order-processing', 'api-backend']; return { generate: ({ from, to }) => { const range = timerange(from, to); - const successfulTimestamps = range.interval('1s').rate(1); + const successfulTimestamps = range.ratePerMinute(60); // `.randomize(3, 180); - const instances = [...Array(numServices).keys()].map((index) => + const instances = services.map((service, index) => apm .service( `${services[index % services.length]}-${languages[index % languages.length]}-${index}`, ENVIRONMENT, languages[index % languages.length] ) - .instance('instance') + .instance(`instance-${index}`) ); const urls = ['GET /order/{id}', 'POST /basket/{id}', 'DELETE /basket', 'GET /products']; const instanceSpans = (instance: Instance, url: string, index: number) => { const successfulTraceEvents = successfulTimestamps.generator((timestamp) => { - const mod = index % 4; - const randomHigh = random(100, mod * 1000); - const randomLow = random(10, randomHigh / 10 + mod * 3); - const duration = random(randomLow, randomHigh); - const childDuration = random(randomLow, duration); + const mod = (index % 4) + 1; + const randomHigh = random(100, mod * 1000, false); + const randomLow = random(10, randomHigh / 10 + mod * 3, false); + const duration = random(randomLow, randomHigh, false); + const childDuration = random(randomLow, duration, false); const remainderDuration = duration - childDuration; const generateError = index % random(mod, 9) === 0; const generateChildError = index % random(mod, 9) === 0; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/many_services.ts b/packages/kbn-apm-synthtrace/src/scenarios/many_services.ts index 9353fe13f6ca6..501d0e678f0f4 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/many_services.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/many_services.ts @@ -29,7 +29,7 @@ const scenario: Scenario = async (runOptions: RunOptions) => { generate: ({ from, to }) => { const range = timerange(from, to); - const successfulTimestamps = range.interval('1s').rate(3); + const successfulTimestamps = range.ratePerMinute(180); const instances = [...Array(numServices).keys()].map((index) => apm @@ -38,7 +38,7 @@ const scenario: Scenario = async (runOptions: RunOptions) => { ENVIRONMENT, languages[index % languages.length] ) - .instance('instance') + .instance(`instance-${index}`) ); const urls = ['GET /order/{id}', 'POST /basket/{id}', 'DELETE /basket', 'GET /products']; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts b/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts index edff51c11a9e4..f8444ab6e5879 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts @@ -27,9 +27,8 @@ const scenario: Scenario = async (runOptions: RunOptions) => { const transactionName = '240rpm/75% 1000ms'; - const successfulTimestamps = range.interval('1s').rate(3); - - const failedTimestamps = range.interval('1s').rate(1); + const successfulTimestamps = range.ratePerMinute(180); + const failedTimestamps = range.ratePerMinute(180); const instances = [...Array(numServices).keys()].map((index) => apm.service(`opbeans-go-${index}`, ENVIRONMENT, 'go').instance('instance') diff --git a/packages/kbn-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts b/packages/kbn-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts index 3d4225cf3f243..afafcc0c49665 100644 --- a/packages/kbn-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts +++ b/packages/kbn-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts @@ -46,6 +46,7 @@ describe('output apm events to elasticsearch', () => { "version": "1.4", }, "observer": Object { + "type": "synthtrace", "version": "8.0.0", "version_major": 8, }, diff --git a/packages/kbn-apm-synthtrace/src/test/rate_per_minute.test.ts b/packages/kbn-apm-synthtrace/src/test/rate_per_minute.test.ts index 73517976737bb..e40848ab9f47b 100644 --- a/packages/kbn-apm-synthtrace/src/test/rate_per_minute.test.ts +++ b/packages/kbn-apm-synthtrace/src/test/rate_per_minute.test.ts @@ -11,21 +11,13 @@ import { apm } from '../lib/apm'; import { timerange } from '../lib/timerange'; import { ApmFields } from '../lib/apm/apm_fields'; +const range = timerange(new Date('2021-01-01T00:00:00.000Z'), new Date('2021-01-01T00:15:00.000Z')); + describe('rate per minute calculations', () => { let iterable: EntityIterable; let arrayIterable: EntityArrayIterable; let events: Array>; - const range = timerange( - new Date('2021-01-01T00:00:00.000Z'), - new Date('2021-01-01T00:15:00.000Z') - ); - - const i1r3 = range.interval('1m').rate(3); - const i5r6 = range.interval('5m').rate(6); - const i30r6 = range.interval('30m').rate(6); - const i1sr3 = range.interval('1s').rate(3); - beforeEach(() => { const javaService = apm.service('opbeans-java', 'production', 'java'); const javaInstance = javaService.instance('instance-1'); @@ -50,26 +42,44 @@ describe('rate per minute calculations', () => { events = iterable.toArray(); arrayIterable = new EntityArrayIterable(events); }); + it('array iterable returns exact rate per minute', () => { - expect(arrayIterable.ratePerMinute()).toEqual(2); + expect(arrayIterable.estimatedRatePerMinute()).toEqual(2); }); it('iterable returns rate per minute approximation', () => { - expect(iterable.ratePerMinute()).toEqual(2); + expect(iterable.estimatedRatePerMinute()).toEqual(2); }); it('iterable returns same rate as materialized iterable', () => { - expect(iterable.ratePerMinute()).toEqual(arrayIterable.ratePerMinute()); + expect(iterable.estimatedRatePerMinute()).toEqual(arrayIterable.estimatedRatePerMinute()); }); +}); +describe('estimatedRatePerMinute', () => { it('interval of 3 per minute returns 3', () => { - expect(i1r3.ratePerMinute()).toEqual(3); + expect(range.interval('1m').rate(3).estimatedRatePerMinute()).toEqual(3); }); + it('interval of 6 per 5 minutes returns 6/5', () => { - expect(i5r6.ratePerMinute()).toEqual(6 / 5); + expect(range.interval('5m').rate(6).estimatedRatePerMinute()).toEqual(6 / 5); }); + it('interval of 6 per 30 minutes returns 6/30', () => { - expect(i30r6.ratePerMinute()).toEqual(6 / 30); + expect(range.interval('30m').rate(6).estimatedRatePerMinute()).toEqual(6 / 30); }); + it('interval of 3 per second returns 60 * 3', () => { - expect(i1sr3.ratePerMinute()).toEqual(60 * 3); + expect(range.interval('1s').rate(3).estimatedRatePerMinute()).toEqual(60 * 3); + }); + + it('ratePerMinute of 180 returns 180', () => { + expect(range.ratePerMinute(180).estimatedRatePerMinute()).toEqual(180); + }); + + it('ratePerMinute of 1 returns 1', () => { + expect(range.ratePerMinute(1).estimatedRatePerMinute()).toEqual(1); + }); + + it('ratePerMinute of 61 returns 61', () => { + expect(range.ratePerMinute(61).estimatedRatePerMinute()).toEqual(61); }); }); diff --git a/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts b/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts index c878f148e4e67..cd791226cc095 100644 --- a/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts +++ b/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts @@ -62,7 +62,7 @@ export interface SpanDocument extends Omit { export interface TransactionDocument extends Omit { service: { name: string; environment: string; version: string }; processor: string; - url: { path: string }; + url: { path: string; query?: string }; http: { request: Request; response: Response; diff --git a/packages/kbn-performance-testing-dataset-extractor/src/request.ts b/packages/kbn-performance-testing-dataset-extractor/src/request.ts index 7c9efaf935742..034c4a276f6ef 100644 --- a/packages/kbn-performance-testing-dataset-extractor/src/request.ts +++ b/packages/kbn-performance-testing-dataset-extractor/src/request.ts @@ -62,6 +62,7 @@ export const getKibanaRequests = ( http: { method: hit.http.request.method, path: hit.url.path, + query: hit.url?.query, headers: combineHeaderFieldValues(hit.http.request.headers), body: payload ? JSON.stringify(strToJSON(payload)) : undefined, statusCode: hit.http.response.status_code, diff --git a/packages/kbn-performance-testing-dataset-extractor/src/types.ts b/packages/kbn-performance-testing-dataset-extractor/src/types.ts index c0dad9757a183..69df8a5fd490c 100644 --- a/packages/kbn-performance-testing-dataset-extractor/src/types.ts +++ b/packages/kbn-performance-testing-dataset-extractor/src/types.ts @@ -17,6 +17,7 @@ export interface Request { http: { method: string; path: string; + query?: string; headers?: { [key: string]: string }; params?: string; body?: JSON | string; diff --git a/renovate.json b/renovate.json index b5523536465fd..8ec367e91ee71 100644 --- a/renovate.json +++ b/renovate.json @@ -27,7 +27,7 @@ "matchPackageNames": ["@elastic/charts"], "reviewers": ["team:datavis", "markov00", "nickofthyme"], "matchBaseBranches": ["main"], - "labels": ["release_note:skip", "auto-backport", "Team:DataVis"], + "labels": ["release_note:skip", "backport:skip", "Team:DataVis"], "draftPR": true, "enabled": true, "assignAutomerge": true, diff --git a/scripts/archive_migration_functions.sh b/scripts/archive_migration_functions.sh index 71260352982a2..a6d9e75271b5c 100755 --- a/scripts/archive_migration_functions.sh +++ b/scripts/archive_migration_functions.sh @@ -1,24 +1,36 @@ #!/bin/bash # ??? Should we migrate -# x-pack/test/functional/es_archives/dashboard/feature_controls/spaces +# x-pack/test/functional/es_archives/spaces/multi_space # ### Yes, it needs migration # ### Saved Object type(s) that we care about: # dashboard # index-pattern # visualization # ### Test file(s) that use it: -# x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts +# x-pack/test/functional/apps/discover/preserve_url.ts +# x-pack/test/functional/apps/visualize/preserve_url.ts +# x-pack/test/functional/apps/dashboard/group1/preserve_url.ts # ### Config(s) that govern the test file(s): # x-pack/test/functional/apps/dashboard/group1/config.ts +# x-pack/test/functional/apps/discover/config.ts +# x-pack/test/functional/apps/visualize/config.ts standard_list="url,index-pattern,query,graph-workspace,tag,visualization,canvas-element,canvas-workpad,dashboard,search,lens,map,cases,uptime-dynamic-settings,osquery-saved-query,osquery-pack,infrastructure-ui-source,metrics-explorer-view,inventory-view,infrastructure-monitoring-log-view,apm-indices" -orig_archive="x-pack/test/functional/es_archives/dashboard/feature_controls/spaces" -new_archive="x-pack/test/functional/fixtures/kbn_archiver/dashboard/feature_controls/custom_space" -newArchives=("x-pack/test/functional/fixtures/kbn_archiver/dashboard/feature_controls/custom_space") -newArchives+=("x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_kibana_non_timezone_space") +orig_archive="x-pack/test/functional/es_archives/spaces/multi_space" +new_archive="x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space" + +# newArchives=("x-pack/test/functional/fixtures/kbn_archiver/dashboard/session_in_space") +# newArchives+=("x-pack/test/functional/fixtures/kbn_archiver/dashboard/session_in_another_space") + +testFiles=("x-pack/test/functional/apps/discover/preserve_url.ts") +testFiles+=("x-pack/test/functional/apps/visualize/preserve_url.ts") +testFiles+=("x-pack/test/functional/apps/dashboard/group1/preserve_url.ts") + test_config="x-pack/test/functional/apps/dashboard/group1/config.ts" +# test_config="x-pack/test/functional/apps/discover/config.ts" +# test_config="x-pack/test/functional/apps/visualize/config.ts" curl_so_count() { local so=${1:-search-session} @@ -356,14 +368,7 @@ save_kbn() { load_kbn() { local space=${1:-default} - - set -x - node scripts/kbn_archiver.js --config "$test_config" load "$new_archive" --space "$space" - set +x -} - -load_kbns() { - local space=${1:-default} + local archive=${2:-${new_archive}} for x in "${newArchives[@]}"; do set -x @@ -379,8 +384,9 @@ load_created_kbn_archive() { } unload_kbn() { + local archive=${1:-${new_archive}} set -x - node scripts/kbn_archiver.js --config "$test_config" unload "$new_archive" + node scripts/kbn_archiver.js --config "$test_config" unload "$archive" set +x } diff --git a/src/core/public/core_system.test.mocks.ts b/src/core/public/core_system.test.mocks.ts index 6018e9765399a..2b378b02554cb 100644 --- a/src/core/public/core_system.test.mocks.ts +++ b/src/core/public/core_system.test.mocks.ts @@ -19,7 +19,7 @@ import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; import { pluginsServiceMock } from './plugins/plugins_service.mock'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; -import { renderingServiceMock } from './rendering/rendering_service.mock'; +import { renderingServiceMock } from '@kbn/core-rendering-browser-mocks'; import { integrationsServiceMock } from '@kbn/core-integrations-browser-mocks'; import { coreAppMock } from './core_app/core_app.mock'; @@ -114,7 +114,7 @@ jest.doMock('@kbn/core-doc-links-browser-internal', () => ({ export const MockRenderingService = renderingServiceMock.create(); export const RenderingServiceConstructor = jest.fn().mockImplementation(() => MockRenderingService); -jest.doMock('./rendering', () => ({ +jest.doMock('@kbn/core-rendering-browser-internal', () => ({ RenderingService: RenderingServiceConstructor, })); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 98784292c7bf0..cf3a9e6405f69 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -37,10 +37,10 @@ import { type InternalApplicationSetup, type InternalApplicationStart, } from '@kbn/core-application-browser-internal'; +import { RenderingService } from '@kbn/core-rendering-browser-internal'; import { fetchOptionalMemoryInfo } from './fetch_optional_memory_info'; import { CoreSetup, CoreStart } from '.'; import { PluginsService } from './plugins'; -import { RenderingService } from './rendering'; import { CoreApp } from './core_app'; import { diff --git a/src/core/public/index.scss b/src/core/public/index.scss index 325265c5c48b0..4b034af74fa1b 100644 --- a/src/core/public/index.scss +++ b/src/core/public/index.scss @@ -1,5 +1,4 @@ @import './variables'; @import './mixins'; @import './core'; -@import './rendering/index'; @import './styles/index'; diff --git a/src/core/public/styles/_index.scss b/src/core/public/styles/_index.scss index 324e238625683..42981c7e07398 100644 --- a/src/core/public/styles/_index.scss +++ b/src/core/public/styles/_index.scss @@ -1,3 +1,4 @@ @import './base'; @import './ace_overrides'; @import './chrome/index'; +@import './rendering/index'; diff --git a/src/core/public/rendering/_base.scss b/src/core/public/styles/rendering/_base.scss similarity index 98% rename from src/core/public/rendering/_base.scss rename to src/core/public/styles/rendering/_base.scss index c97afbf14a8f6..80638711f0335 100644 --- a/src/core/public/rendering/_base.scss +++ b/src/core/public/styles/rendering/_base.scss @@ -1,4 +1,4 @@ -@import '../mixins'; +@import '../../mixins'; /** * Stretch the root element of the Kibana application to set the base-size that diff --git a/src/core/public/rendering/_index.scss b/src/core/public/styles/rendering/_index.scss similarity index 100% rename from src/core/public/rendering/_index.scss rename to src/core/public/styles/rendering/_index.scss diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index f40c129cd96ae..9a73f1f295492 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -295,6 +295,7 @@ kibana_vars=( xpack.ingestManager.registryUrl xpack.observability.annotations.index xpack.observability.unsafe.slo.enabled + xpack.observability.unsafe.alertDetails.enabled xpack.reporting.capture.browser.autoDownload xpack.reporting.capture.browser.chromium.disableSandbox xpack.reporting.capture.browser.chromium.inspect @@ -341,6 +342,7 @@ kibana_vars=( xpack.reporting.roles.allow xpack.reporting.roles.enabled xpack.ruleRegistry.write.enabled + xpack.security.accessAgreement.message xpack.security.audit.appender.fileName xpack.security.audit.appender.layout.highlight xpack.security.audit.appender.layout.pattern diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 493ba08ee9542..387d1a077a747 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -316,7 +316,7 @@ describe('XYChart component', () => { const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - expect(axisStyle).toBe(3); + expect(axisStyle).toBe(2); }); test('it should disable the new time axis for a vertical bar with break down dimension', () => { const timeLayer: DataLayerConfig = { @@ -366,7 +366,7 @@ describe('XYChart component', () => { const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - expect(axisStyle).toBe(3); + expect(axisStyle).toBe(2); }); }); describe('endzones', () => { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 0d6c21506a79f..62f67549f7df6 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -927,7 +927,7 @@ export function XYChart({ style={xAxisStyle} showOverlappingLabels={xAxisConfig?.showOverlappingLabels} showDuplicatedTicks={xAxisConfig?.showDuplicates} - timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} + timeAxisLayerCount={shouldUseNewTimeAxis ? 2 : 0} /> {isSplitChart && splitTable && ( ); diff --git a/test/functional/apps/discover/group2/_data_grid_doc_table.ts b/test/functional/apps/discover/group2/_data_grid_doc_table.ts index fbc6b48242b7b..c2f55847e7d1e 100644 --- a/test/functional/apps/discover/group2/_data_grid_doc_table.ts +++ b/test/functional/apps/discover/group2/_data_grid_doc_table.ts @@ -92,7 +92,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug(`expanded document id: ${expandDocId}`); await dataGrid.clickRowToggle(); - await find.clickByCssSelectorWhenNotDisabled('#kbn_doc_viewer_tab_1'); + await find.clickByCssSelectorWhenNotDisabledWithoutRetry('#kbn_doc_viewer_tab_1'); await retry.waitForWithTimeout( 'document id in flyout matching the expanded document id', @@ -140,7 +140,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug(`expanded document id: ${expandDocId}`); await dataGrid.clickRowToggle(); - await find.clickByCssSelectorWhenNotDisabled('#kbn_doc_viewer_tab_1'); + await find.clickByCssSelectorWhenNotDisabledWithoutRetry('#kbn_doc_viewer_tab_1'); await retry.waitForWithTimeout( 'document id in flyout matching the expanded document id', diff --git a/test/functional/apps/visualize/replaced_vislib_chart_types/_area_chart.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_area_chart.ts index 309d1abb9961b..3e765baf3ba64 100644 --- a/test/functional/apps/visualize/replaced_vislib_chart_types/_area_chart.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_area_chart.ts @@ -436,7 +436,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('interval errors', () => { before(async () => { // to trigger displaying of error messages - await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('visualizeEditorRenderButton'); // this will avoid issues with the play tooltip covering the interval field await testSubjects.scrollIntoView('advancedParams-2'); }); @@ -520,7 +520,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.setInterval('Millisecond'); // Apply interval - await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('visualizeEditorRenderButton'); const isHelperScaledLabelExists = await find.existsByCssSelector( '[data-test-subj="currentlyScaledText"]' @@ -537,7 +537,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should update scaled label text after custom interval is set and time range is changed', async () => { await PageObjects.visEditor.setInterval('10s', { type: 'custom' }); - await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('visualizeEditorRenderButton'); const isHelperScaledLabelExists = await find.existsByCssSelector( '[data-test-subj="currentlyScaledText"]' ); diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index da301a7d5d82d..4fe9e8c33ecdc 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -658,7 +658,7 @@ export class DiscoverPageObject extends FtrService { public async clickViewModeFieldStatsButton() { await this.retry.tryForTime(2 * 1000, async () => { await this.testSubjects.existOrFail('dscViewModeFieldStatsButton'); - await this.testSubjects.clickWhenNotDisabled('dscViewModeFieldStatsButton'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('dscViewModeFieldStatsButton'); await this.testSubjects.existOrFail('dscFieldStatsEmbeddedContent'); }); } diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 350e2cf76599f..ab47fb31fea54 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -306,7 +306,7 @@ export class SettingsPageObject extends FtrService { } async clearFieldTypeFilter(type: string) { - await this.testSubjects.clickWhenNotDisabled('indexedFieldTypeFilterDropdown'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('indexedFieldTypeFilterDropdown'); await this.retry.try(async () => { await this.testSubjects.existOrFail('indexedFieldTypeFilterDropdown-popover'); }); @@ -319,7 +319,7 @@ export class SettingsPageObject extends FtrService { } async setFieldTypeFilter(type: string) { - await this.testSubjects.clickWhenNotDisabled('indexedFieldTypeFilterDropdown'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('indexedFieldTypeFilterDropdown'); await this.testSubjects.existOrFail('indexedFieldTypeFilterDropdown-popover'); await this.testSubjects.existOrFail(`indexedFieldTypeFilterDropdown-option-${type}`); await this.testSubjects.click(`indexedFieldTypeFilterDropdown-option-${type}`); @@ -328,7 +328,7 @@ export class SettingsPageObject extends FtrService { } async clearScriptedFieldLanguageFilter(type: string) { - await this.testSubjects.clickWhenNotDisabled('scriptedFieldLanguageFilterDropdown'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('scriptedFieldLanguageFilterDropdown'); await this.retry.try(async () => { await this.testSubjects.existOrFail('scriptedFieldLanguageFilterDropdown-popover'); }); @@ -344,7 +344,9 @@ export class SettingsPageObject extends FtrService { async setScriptedFieldLanguageFilter(language: string) { await this.retry.try(async () => { - await this.testSubjects.clickWhenNotDisabled('scriptedFieldLanguageFilterDropdown'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry( + 'scriptedFieldLanguageFilterDropdown' + ); return await this.find.byCssSelector('div.euiPopover__panel[data-popover-open]'); }); await this.testSubjects.existOrFail('scriptedFieldLanguageFilterDropdown-popover'); diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 377c844f7a446..dc12e8ae8fa59 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -265,7 +265,7 @@ export class VisualBuilderPageObject extends FtrService { } public async applyChanges() { - await this.testSubjects.clickWhenNotDisabled('applyBtn'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('applyBtn'); } /** diff --git a/test/functional/page_objects/visualize_editor_page.ts b/test/functional/page_objects/visualize_editor_page.ts index cd683256c623a..1700b82782580 100644 --- a/test/functional/page_objects/visualize_editor_page.ts +++ b/test/functional/page_objects/visualize_editor_page.ts @@ -59,7 +59,7 @@ export class VisualizeEditorPageObject extends FtrService { } public async inputControlSubmit() { - await this.testSubjects.clickWhenNotDisabled('inputControlSubmitBtn'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('inputControlSubmitBtn'); await this.visChart.waitForVisualizationRenderingStabilized(); } @@ -70,7 +70,7 @@ export class VisualizeEditorPageObject extends FtrService { const prevRenderingCount = await this.visChart.getVisualizationRenderingCount(); this.log.debug(`Before Rendering count ${prevRenderingCount}`); - await this.testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('visualizeEditorRenderButton'); await this.visChart.waitForRenderingCount(prevRenderingCount + 1); } diff --git a/test/functional/services/common/find.ts b/test/functional/services/common/find.ts index 978060e9423f2..da12279a3ffa1 100644 --- a/test/functional/services/common/find.ts +++ b/test/functional/services/common/find.ts @@ -10,7 +10,9 @@ import { WebDriver, WebElement, By, until } from 'selenium-webdriver'; import { Browsers } from '../remote/browsers'; import { FtrService, FtrProviderContext } from '../../ftr_provider_context'; +import { retryOnStale } from './retry_on_stale'; import { WebElementWrapper } from '../lib/web_element_wrapper'; +import { TimeoutOpt } from './types'; export class FindService extends FtrService { private readonly log = this.ctx.getService('log'); @@ -285,16 +287,33 @@ export class FindService extends FtrService { }, timeout); } - public async clickByCssSelectorWhenNotDisabled( + public async clickByCssSelectorWhenNotDisabled(selector: string, opts?: TimeoutOpt) { + const timeout = opts?.timeout ?? this.defaultFindTimeout; + + await retryOnStale(this.log, async () => { + this.log.debug(`Find.clickByCssSelectorWhenNotDisabled(${selector}, timeout=${timeout})`); + + const element = await this.byCssSelector(selector); + await element.moveMouseTo(); + await this.driver.wait(until.elementIsEnabled(element._webElement), timeout); + await element.click(); + }); + } + + public async clickByCssSelectorWhenNotDisabledWithoutRetry( selector: string, - { timeout } = { timeout: this.defaultFindTimeout } + opts?: TimeoutOpt ): Promise { - this.log.debug(`Find.clickByCssSelectorWhenNotDisabled('${selector}') with timeout=${timeout}`); + const timeout = opts?.timeout ?? this.defaultFindTimeout; + + this.log.debug( + `Find.clickByCssSelectorWhenNotDisabledWithoutRetry(${selector}, timeout=${timeout})` + ); // Don't wrap this code in a retry, or stale element checks may get caught here and the element // will never be re-grabbed. Let errors bubble, but continue checking for disabled property until // it's gone. - const element = await this.byCssSelector(selector, timeout); + const element = await this.byCssSelector(selector); await element.moveMouseTo(); await this.driver.wait(until.elementIsEnabled(element._webElement), timeout); await element.click(); diff --git a/test/functional/services/common/retry_on_stale.ts b/test/functional/services/common/retry_on_stale.ts new file mode 100644 index 0000000000000..a240e8031cd68 --- /dev/null +++ b/test/functional/services/common/retry_on_stale.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ToolingLog } from '@kbn/tooling-log'; + +const MAX_ATTEMPTS = 10; + +const isObj = (v: unknown): v is Record => typeof v === 'object' && v !== null; +const errMsg = (err: unknown) => (isObj(err) && typeof err.message === 'string' ? err.message : ''); + +export async function retryOnStale(log: ToolingLog, fn: () => Promise): Promise { + let attempt = 0; + while (true) { + attempt += 1; + try { + return await fn(); + } catch (error) { + if (errMsg(error).includes('stale element reference')) { + if (attempt >= MAX_ATTEMPTS) { + throw new Error(`retryOnStale ran out of attempts after ${attempt} tries`); + } + + log.warning('stale element exception caught, retrying'); + continue; + } + + throw error; + } + } +} diff --git a/test/functional/services/common/test_subjects.ts b/test/functional/services/common/test_subjects.ts index d6254b04c1641..0e9d7ed9ce931 100644 --- a/test/functional/services/common/test_subjects.ts +++ b/test/functional/services/common/test_subjects.ts @@ -9,6 +9,7 @@ import testSubjSelector from '@kbn/test-subj-selector'; import { WebElementWrapper } from '../lib/web_element_wrapper'; import { FtrService } from '../../ftr_provider_context'; +import { TimeoutOpt } from './types'; interface ExistsOptions { timeout?: number; @@ -111,14 +112,33 @@ export class TestSubjects extends FtrService { await input.type(text); } - public async clickWhenNotDisabled( + /** + * Clicks on the element identified by the testSubject selector. If the retries + * automatically on "stale element" errors unlike clickWhenNotDisabledWithoutRetry. + * `opts.timeout` defaults to the 'timeouts.find' config, which defaults to 10 seconds + */ + public async clickWhenNotDisabled(selector: string, opts?: TimeoutOpt) { + this.log.debug(`TestSubjects.clickWhenNotDisabled(${selector})`); + await this.findService.clickByCssSelectorWhenNotDisabled(testSubjSelector(selector), opts); + } + + /** + * Clicks on the element identified by the testSubject selector. Somewhat surprisingly, + * this method allows `stale element` errors to propogate, which is why it was renamed + * from `clickWhenNotDisabled()` and that method was re-implemented to be more consistent + * with the rest of the functions in this service. + * + * `opts.timeout` defaults to the 'timeouts.find' config, which defaults to 10 seconds + */ + public async clickWhenNotDisabledWithoutRetry( selector: string, - { timeout = this.FIND_TIME }: { timeout?: number } = {} + opts?: TimeoutOpt ): Promise { - this.log.debug(`TestSubjects.clickWhenNotDisabled(${selector})`); - await this.findService.clickByCssSelectorWhenNotDisabled(testSubjSelector(selector), { - timeout, - }); + this.log.debug(`TestSubjects.clickWhenNotDisabledWithoutRetry(${selector})`); + await this.findService.clickByCssSelectorWhenNotDisabledWithoutRetry( + testSubjSelector(selector), + opts + ); } public async click( diff --git a/test/functional/services/common/types.ts b/test/functional/services/common/types.ts new file mode 100644 index 0000000000000..b988ba8f66eab --- /dev/null +++ b/test/functional/services/common/types.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface TimeoutOpt { + timeout?: number; +} diff --git a/test/functional/services/dashboard/panel_actions.ts b/test/functional/services/dashboard/panel_actions.ts index efffdaf1e7782..17c2ffee733b2 100644 --- a/test/functional/services/dashboard/panel_actions.ts +++ b/test/functional/services/dashboard/panel_actions.ts @@ -79,7 +79,7 @@ export class DashboardPanelActionsService extends FtrService { await this.openContextMenu(); const isActionVisible = await this.testSubjects.exists(EDIT_PANEL_DATA_TEST_SUBJ); if (!isActionVisible) await this.clickContextMenuMoreItem(); - await this.testSubjects.clickWhenNotDisabled(EDIT_PANEL_DATA_TEST_SUBJ); + await this.testSubjects.clickWhenNotDisabledWithoutRetry(EDIT_PANEL_DATA_TEST_SUBJ); await this.header.waitUntilLoadingHasFinished(); await this.common.waitForTopNavToBeVisible(); } @@ -92,7 +92,7 @@ export class DashboardPanelActionsService extends FtrService { } else { await this.openContextMenu(); } - await this.testSubjects.clickWhenNotDisabled(EDIT_PANEL_DATA_TEST_SUBJ); + await this.testSubjects.clickWhenNotDisabledWithoutRetry(EDIT_PANEL_DATA_TEST_SUBJ); } async clickExpandPanelToggle() { diff --git a/test/functional/services/field_editor.ts b/test/functional/services/field_editor.ts index 3014ec79941c5..68fc3707ca3a9 100644 --- a/test/functional/services/field_editor.ts +++ b/test/functional/services/field_editor.ts @@ -53,14 +53,18 @@ export class FieldEditorService extends FtrService { public async confirmSave() { await this.retry.try(async () => { await this.testSubjects.setValue('saveModalConfirmText', 'change'); - await this.testSubjects.clickWhenNotDisabled('confirmModalConfirmButton', { timeout: 1000 }); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('confirmModalConfirmButton', { + timeout: 1000, + }); }); } public async confirmDelete() { await this.retry.try(async () => { await this.testSubjects.setValue('deleteModalConfirmText', 'remove'); - await this.testSubjects.clickWhenNotDisabled('confirmModalConfirmButton', { timeout: 1000 }); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('confirmModalConfirmButton', { + timeout: 1000, + }); }); } } diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index 07ad11d32d4ca..906b72a438d63 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -161,7 +161,7 @@ export class FilterBarService extends FtrService { } } - await this.testSubjects.clickWhenNotDisabled('saveFilter'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('saveFilter'); }); await this.header.awaitGlobalLoadingIndicatorHidden(); } diff --git a/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts b/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts index bf33c24f0239d..7616583de2e66 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts @@ -18,7 +18,8 @@ export default function ({ }: FtrProviderContext & { updateBaselines: boolean }) { let expectExpression: ExpectExpression; - describe('fetch event annotation tests', () => { + // Failing: See https://github.com/elastic/kibana/issues/140113 + describe.skip('fetch event annotation tests', () => { before(() => { expectExpression = expectExpressionProvider({ getService, updateBaselines }); }); diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 51f6d0aa4bbf3..ce598ee1e7dcf 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -221,6 +221,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.trigger_actions_ui.enableGeoTrackingThresholdAlert (boolean)', 'xpack.upgrade_assistant.readonly (boolean)', 'xpack.upgrade_assistant.ui.enabled (boolean)', + 'xpack.observability.unsafe.alertDetails.enabled (boolean)', + 'xpack.observability.unsafe.slo.enabled (boolean)', ]; // We don't assert that actualExposedConfigKeys and expectedExposedConfigKeys are equal, because test failure messages with large // arrays are hard to grok. Instead, we take the difference between the two arrays and assert them separately, that way it's diff --git a/x-pack/README.md b/x-pack/README.md index dad469295ca0f..9c3d0046a77e7 100644 --- a/x-pack/README.md +++ b/x-pack/README.md @@ -4,6 +4,17 @@ This directory tree contains files subject to the Elastic License 2.0. The files to the Elastic License 2.0 are grouped in this directory to clearly separate them from files dual-licensed under the Server Side Public License and the Elastic License 2.0. +## Alert Details page (feature flag) + +If you have: + +```yaml +xpack.observability.unsafe.alertDetails.enabled: true +``` +In Kibana configuration, will allow the user to navigate to the new Alert Details page, instead of the Alert Flyout when clicking on `View alert details` in the Alert table + + + # Development By default, Kibana will run with X-Pack installed as mentioned in the [contributing guide](../CONTRIBUTING.md). diff --git a/x-pack/examples/files_example/public/types.ts b/x-pack/examples/files_example/public/types.ts index 2d450fbdad111..016a14286cf01 100644 --- a/x-pack/examples/files_example/public/types.ts +++ b/x-pack/examples/files_example/public/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { FilesSetup, FilesStart, FilesClient } from '@kbn/files-plugin/public'; +import type { FilesSetup, FilesStart, ScopedFilesClient } from '@kbn/files-plugin/public'; export interface FilesExamplePluginsSetup { files: FilesSetup; @@ -17,7 +17,7 @@ export interface FilesExamplePluginsStart { export interface FileClients { // Example file kind - example: FilesClient; + example: ScopedFilesClient; } export interface AppPluginStartDependencies { 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 9b8b95e8af36f..30013e212c526 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 @@ -8,7 +8,7 @@ import sinon from 'sinon'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { Alert } from './alert'; -import { createAlertFactory } from './create_alert_factory'; +import { createAlertFactory, getPublicAlertFactory } from './create_alert_factory'; import { processAlerts } from '../lib'; jest.mock('../lib', () => ({ @@ -238,4 +238,78 @@ describe('createAlertFactory()', () => { `Set doesSetRecoveryContext to true on rule type to get access to recovered alerts.` ); }); + + test('throws error when checking limit usage if alertLimit.getValue is called but alertLimit.setLimitReached is not', () => { + const alertFactory = createAlertFactory({ + alerts: {}, + logger, + maxAlerts: 1000, + }); + + const limit = alertFactory.alertLimit.getValue(); + expect(limit).toEqual(1000); + + expect(() => { + alertFactory.alertLimit.checkLimitUsage(); + }).toThrowErrorMatchingInlineSnapshot( + `"Rule has not reported whether alert limit has been reached after requesting limit value!"` + ); + }); + + test('does not throw error when checking limit usage if alertLimit.getValue is called and alertLimit.setLimitReached is called with reached = true', () => { + const alertFactory = createAlertFactory({ + alerts: {}, + logger, + maxAlerts: 1000, + }); + + const limit = alertFactory.alertLimit.getValue(); + expect(limit).toEqual(1000); + + alertFactory.alertLimit.setLimitReached(true); + alertFactory.alertLimit.checkLimitUsage(); + }); + + test('does not throw error when checking limit usage if alertLimit.getValue is called and alertLimit.setLimitReached is called with reached = false', () => { + const alertFactory = createAlertFactory({ + alerts: {}, + logger, + maxAlerts: 1000, + }); + + const limit = alertFactory.alertLimit.getValue(); + expect(limit).toEqual(1000); + + alertFactory.alertLimit.setLimitReached(false); + alertFactory.alertLimit.checkLimitUsage(); + }); +}); + +describe('getPublicAlertFactory', () => { + test('only returns subset of function from given alert factory', () => { + const alertFactory = createAlertFactory({ + alerts: {}, + logger, + maxAlerts: 1000, + }); + + expect(alertFactory.create).toBeDefined(); + expect(alertFactory.alertLimit.getValue).toBeDefined(); + expect(alertFactory.alertLimit.setLimitReached).toBeDefined(); + expect(alertFactory.alertLimit.checkLimitUsage).toBeDefined(); + expect(alertFactory.hasReachedAlertLimit).toBeDefined(); + expect(alertFactory.done).toBeDefined(); + + const publicAlertFactory = getPublicAlertFactory(alertFactory); + + expect(publicAlertFactory.create).toBeDefined(); + expect(publicAlertFactory.done).toBeDefined(); + expect(publicAlertFactory.alertLimit.getValue).toBeDefined(); + expect(publicAlertFactory.alertLimit.setLimitReached).toBeDefined(); + + // @ts-expect-error + expect(publicAlertFactory.alertLimit.checkLimitUsage).not.toBeDefined(); + // @ts-expect-error + expect(publicAlertFactory.hasReachedAlertLimit).not.toBeDefined(); + }); }); diff --git a/x-pack/plugins/alerting/server/alert/create_alert_factory.ts b/x-pack/plugins/alerting/server/alert/create_alert_factory.ts index 158926a19d782..e0d3ecc2690fc 100644 --- a/x-pack/plugins/alerting/server/alert/create_alert_factory.ts +++ b/x-pack/plugins/alerting/server/alert/create_alert_factory.ts @@ -11,6 +11,32 @@ import { AlertInstanceContext, AlertInstanceState } from '../types'; import { Alert, PublicAlert } from './alert'; import { processAlerts } from '../lib'; +export interface AlertFactory< + State extends AlertInstanceState, + Context extends AlertInstanceContext, + ActionGroupIds extends string +> { + create: (id: string) => PublicAlert; + alertLimit: { + getValue: () => number; + setLimitReached: (reached: boolean) => void; + checkLimitUsage: () => void; + }; + hasReachedAlertLimit: () => boolean; + done: () => AlertFactoryDoneUtils; +} + +export type PublicAlertFactory< + State extends AlertInstanceState, + Context extends AlertInstanceContext, + ActionGroupIds extends string +> = Pick, 'create' | 'done'> & { + alertLimit: Pick< + AlertFactory['alertLimit'], + 'getValue' | 'setLimitReached' + >; +}; + export interface AlertFactoryDoneUtils< State extends AlertInstanceState, Context extends AlertInstanceContext, @@ -38,7 +64,7 @@ export function createAlertFactory< logger, maxAlerts, canSetRecoveryContext = false, -}: CreateAlertFactoryOpts) { +}: CreateAlertFactoryOpts): AlertFactory { // Keep track of which alerts we started with so we can determine which have recovered const originalAlerts = cloneDeep(alerts); @@ -48,6 +74,12 @@ export function createAlertFactory< // Whether the number of alerts reported has reached max allowed let hasReachedAlertLimit = false; + // Whether rule type has asked for the alert limit + let hasRequestedAlertLimit = false; + + // Whether rule type has reported back if alert limit was reached + let hasReportedLimitReached = false; + let isDone = false; return { create: (id: string): PublicAlert => { @@ -66,6 +98,25 @@ export function createAlertFactory< return alerts[id]; }, + // namespace alert limit services for rule type executors to use + alertLimit: { + getValue: (): number => { + hasRequestedAlertLimit = true; + return maxAlerts; + }, + setLimitReached: (reached: boolean) => { + hasReportedLimitReached = true; + hasReachedAlertLimit = reached; + }, + checkLimitUsage: () => { + // If the rule type has requested the value but never reported back, throw an error + if (hasRequestedAlertLimit && !hasReportedLimitReached) { + throw new Error( + `Rule has not reported whether alert limit has been reached after requesting limit value!` + ); + } + }, + }, hasReachedAlertLimit: (): boolean => hasReachedAlertLimit, done: (): AlertFactoryDoneUtils => { isDone = true; @@ -94,3 +145,20 @@ export function createAlertFactory< }, }; } + +export function getPublicAlertFactory< + State extends AlertInstanceState = AlertInstanceState, + Context extends AlertInstanceContext = AlertInstanceContext, + ActionGroupIds extends string = string +>( + alertFactory: AlertFactory +): PublicAlertFactory { + return { + create: (...args): PublicAlert => alertFactory.create(...args), + alertLimit: { + getValue: (): number => alertFactory.alertLimit.getValue(), + setLimitReached: (...args): void => alertFactory.alertLimit.setLimitReached(...args), + }, + done: (): AlertFactoryDoneUtils => alertFactory.done(), + }; +} diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerting/server/mocks.ts index 7abdf04296beb..b5e1a96d300dc 100644 --- a/x-pack/plugins/alerting/server/mocks.ts +++ b/x-pack/plugins/alerting/server/mocks.ts @@ -104,7 +104,10 @@ const createRuleExecutorServicesMock = < return { alertFactory: { create: jest.fn().mockReturnValue(alertFactoryMockCreate), - hasReachedAlertLimit: jest.fn().mockReturnValue(false), + alertLimit: { + getValue: jest.fn().mockReturnValue(1000), + setLimitReached: jest.fn(), + }, done: jest.fn().mockReturnValue(alertFactoryMockDone), }, savedObjectsClient: savedObjectsClientMock.create(), 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 4866ace8bfecd..3138d3aa9d087 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -67,6 +67,7 @@ import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event import { loadRule } from './rule_loader'; import { logAlerts } from './log_alerts'; import { scheduleActionsForAlerts } from './schedule_actions_for_alerts'; +import { getPublicAlertFactory } from '../alert/create_alert_factory'; import { TaskRunnerTimer, TaskRunnerTimerSpan } from './task_runner_timer'; const FALLBACK_RETRY_INTERVAL = '5m'; @@ -317,6 +318,18 @@ export class TaskRunner< maxAlerts: this.maxAlerts, canSetRecoveryContext: ruleType.doesSetRecoveryContext ?? false, }); + + const checkHasReachedAlertLimit = () => { + const reachedLimit = alertFactory.hasReachedAlertLimit(); + if (reachedLimit) { + this.logger.warn( + `rule execution generated greater than ${this.maxAlerts} alerts: ${ruleLabel}` + ); + ruleRunMetricsStore.setHasReachedAlertLimit(true); + } + return reachedLimit; + }; + let updatedState: void | Record; try { const ctx = { @@ -341,7 +354,7 @@ export class TaskRunner< searchSourceClient: wrappedSearchSourceClient.searchSourceClient, uiSettingsClient: this.context.uiSettings.asScopedToClient(savedObjectsClient), scopedClusterClient: wrappedScopedClusterClient.client(), - alertFactory, + alertFactory: getPublicAlertFactory(alertFactory), shouldWriteAlerts: () => this.shouldLogAndScheduleActionsForAlerts(), shouldStopExecution: () => this.cancelled, }, @@ -374,14 +387,16 @@ export class TaskRunner< }, }) ); + + // Rule type execution has successfully completed + // Check that the rule type either never requested the max alerts limit + // or requested it and then reported back whether it exceeded the limit + // If neither of these apply, this check will throw an error + // These errors should show up during rule type development + alertFactory.alertLimit.checkLimitUsage(); } catch (err) { // Check if this error is due to reaching the alert limit - if (alertFactory.hasReachedAlertLimit()) { - this.logger.warn( - `rule execution generated greater than ${this.maxAlerts} alerts: ${ruleLabel}` - ); - ruleRunMetricsStore.setHasReachedAlertLimit(true); - } else { + if (!checkHasReachedAlertLimit()) { this.alertingEventLogger.setExecutionFailed( `rule execution failure: ${ruleLabel}`, err.message @@ -394,6 +409,9 @@ export class TaskRunner< } } + // Check if the rule type has reported that it reached the alert limit + checkHasReachedAlertLimit(); + this.alertingEventLogger.setExecutionSucceeded(`rule executed: ${ruleLabel}`); ruleRunMetricsStore.setSearchMetrics([ diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index efce3583e1879..632d0489a9bb7 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -19,7 +19,6 @@ import { SavedObjectsClientContract, } from '@kbn/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import { AlertFactoryDoneUtils, PublicAlert } from './alert'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { RulesClient } from './rules_client'; @@ -44,6 +43,7 @@ import { MappedParams, RuleSnooze, } from '../common'; +import { PublicAlertFactory } from './alert/create_alert_factory'; export type WithoutQueryAndParams = Pick>; export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined; export type { RuleTypeParams }; @@ -68,21 +68,16 @@ export type AlertingRequestHandlerContext = CustomRequestHandlerContext<{ * @internal */ export type AlertingRouter = IRouter; - export interface RuleExecutorServices< - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext, + State extends AlertInstanceState = AlertInstanceState, + Context extends AlertInstanceContext = AlertInstanceContext, ActionGroupIds extends string = never > { searchSourceClient: ISearchStartSearchSource; savedObjectsClient: SavedObjectsClientContract; uiSettingsClient: IUiSettingsClient; scopedClusterClient: IScopedClusterClient; - alertFactory: { - create: (id: string) => PublicAlert; - hasReachedAlertLimit: () => boolean; - done: () => AlertFactoryDoneUtils; - }; + alertFactory: PublicAlertFactory; shouldWriteAlerts: () => boolean; shouldStopExecution: () => boolean; } diff --git a/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap index 1c7d4dddab38c..fb5d820091ab0 100644 --- a/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap @@ -83,6 +83,8 @@ exports[`Error HTTP_REQUEST_METHOD 1`] = `undefined`; exports[`Error HTTP_RESPONSE_STATUS_CODE 1`] = `undefined`; +exports[`Error INDEX 1`] = `undefined`; + exports[`Error KUBERNETES 1`] = `undefined`; exports[`Error LABEL_NAME 1`] = `undefined`; @@ -205,6 +207,8 @@ exports[`Error SPAN_SUBTYPE 1`] = `undefined`; exports[`Error SPAN_TYPE 1`] = `undefined`; +exports[`Error TIER 1`] = `undefined`; + exports[`Error TRACE_ID 1`] = `"trace id"`; exports[`Error TRANSACTION_DURATION 1`] = `undefined`; @@ -318,6 +322,8 @@ exports[`Span HTTP_REQUEST_METHOD 1`] = `undefined`; exports[`Span HTTP_RESPONSE_STATUS_CODE 1`] = `undefined`; +exports[`Span INDEX 1`] = `undefined`; + exports[`Span KUBERNETES 1`] = `undefined`; exports[`Span LABEL_NAME 1`] = `undefined`; @@ -436,6 +442,8 @@ exports[`Span SPAN_SUBTYPE 1`] = `"my subtype"`; exports[`Span SPAN_TYPE 1`] = `"span type"`; +exports[`Span TIER 1`] = `undefined`; + exports[`Span TRACE_ID 1`] = `"trace id"`; exports[`Span TRANSACTION_DURATION 1`] = `undefined`; @@ -553,6 +561,8 @@ exports[`Transaction HTTP_REQUEST_METHOD 1`] = `"GET"`; exports[`Transaction HTTP_RESPONSE_STATUS_CODE 1`] = `200`; +exports[`Transaction INDEX 1`] = `undefined`; + exports[`Transaction KUBERNETES 1`] = ` Object { "pod": Object { @@ -681,6 +691,8 @@ exports[`Transaction SPAN_SUBTYPE 1`] = `undefined`; exports[`Transaction SPAN_TYPE 1`] = `undefined`; +exports[`Transaction TIER 1`] = `undefined`; + exports[`Transaction TRACE_ID 1`] = `"trace id"`; exports[`Transaction TRANSACTION_DURATION 1`] = `1337`; diff --git a/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts b/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts index 43d58a6f60a98..0a6399c34ae65 100644 --- a/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts +++ b/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts @@ -147,3 +147,7 @@ export const PROFILE_INUSE_SPACE = 'profile.inuse_space.bytes'; export const FAAS_ID = 'faas.id'; export const FAAS_COLDSTART = 'faas.coldstart'; export const FAAS_TRIGGER_TYPE = 'faas.trigger.type'; + +// Metadata +export const TIER = '_tier'; +export const INDEX = '_index'; diff --git a/x-pack/plugins/apm/common/storage_explorer_types.ts b/x-pack/plugins/apm/common/storage_explorer_types.ts new file mode 100644 index 0000000000000..579417a0f8e03 --- /dev/null +++ b/x-pack/plugins/apm/common/storage_explorer_types.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export enum IndexLifecyclePhaseSelectOption { + All = 'all', + Hot = 'hot', + Warm = 'warm', + Cold = 'cold', + Frozen = 'frozen', +} + +export const indexLifeCyclePhaseToDataTier = { + [IndexLifecyclePhaseSelectOption.Hot]: 'data_hot', + [IndexLifecyclePhaseSelectOption.Warm]: 'data_warm', + [IndexLifecyclePhaseSelectOption.Cold]: 'data_cold', + [IndexLifecyclePhaseSelectOption.Frozen]: 'data_frozen', +}; + +export const indexLifecyclePhaseRt = t.type({ + indexLifecyclePhase: t.union([ + t.literal(IndexLifecyclePhaseSelectOption.All), + t.literal(IndexLifecyclePhaseSelectOption.Hot), + t.literal(IndexLifecyclePhaseSelectOption.Warm), + t.literal(IndexLifecyclePhaseSelectOption.Cold), + t.literal(IndexLifecyclePhaseSelectOption.Frozen), + ]), +}); diff --git a/x-pack/plugins/apm/dev_docs/routing_and_linking.md b/x-pack/plugins/apm/dev_docs/routing_and_linking.md index d5c1d6c630635..19087d12dbc9a 100644 --- a/x-pack/plugins/apm/dev_docs/routing_and_linking.md +++ b/x-pack/plugins/apm/dev_docs/routing_and_linking.md @@ -72,7 +72,7 @@ const serviceOverviewLink = apmRouter.link('/services/:serviceName', { If you're not in React context, you can also import `apmRouter` directly and call its `link` function - but you have to prepend the basePath manually in that case. -We also have the [`getLegacyApmHref` function and `APMLink` component](../public/components/shared/links/apm/APMLink.tsx), but we should consider them deprecated, in favor of `router.link`. Other components inside that directory contain other functions and components that provide the same functionality for linking to more specific sections inside the APM plugin. +We also have the [`getLegacyApmHref` function and `LegacyAPMLink` component](../public/components/shared/links/apm/apm_link.tsx), but we should consider them deprecated, in favor of `router.link`. Other components inside that directory contain other functions and components that provide the same functionality for linking to more specific sections inside the APM plugin. ### Cross-app linking diff --git a/x-pack/plugins/apm/dev_docs/testing.md b/x-pack/plugins/apm/dev_docs/testing.md index c204a3922139f..62fec34701cf9 100644 --- a/x-pack/plugins/apm/dev_docs/testing.md +++ b/x-pack/plugins/apm/dev_docs/testing.md @@ -72,6 +72,8 @@ node scripts/test/api --runner --basic --updateSnapshots The E2E tests are located in [`x-pack/plugins/apm/ftr_e2e`](../ftr_e2e) +[Test tips and best practices](../ftr_e2e/README.md) + ### Start test server ``` @@ -81,7 +83,7 @@ node x-pack/plugins/apm/scripts/test/e2e.js --server ### Run tests ``` -node x-pack/plugins/apm/scripts/test/e2e.js --open +node x-pack/plugins/apm/scripts/test/e2e.js --runner --open ``` ### A11y checks diff --git a/x-pack/plugins/apm/ftr_e2e/README.md b/x-pack/plugins/apm/ftr_e2e/README.md index 96d6671bb3699..f62040babfe12 100644 --- a/x-pack/plugins/apm/ftr_e2e/README.md +++ b/x-pack/plugins/apm/ftr_e2e/README.md @@ -2,6 +2,162 @@ APM uses [FTR](../../../../packages/kbn-test/README.md) (functional test runner) and [Cypress](https://www.cypress.io/) to run the e2e tests. The tests are located at `kibana/x-pack/plugins/apm/ftr_e2e/cypress/integration`. +## Tips and best practices + +### Don't `await` Cypress methods + +Given this backend task: + +```ts +// plugins.ts +const plugin: Cypress.PluginConfig = (on, config) => { + on('task', { + async waitForMe(ms: number) { + return new Promise((resolve) => { + setTimeout(() => resolve(null), ms); + }); + } + } +}; +``` + +**WRONG** + +Intuitively an async task should be `await`'ed. + +```ts +// feature.spec.ts +beforeEach(async () => { + await cy.task('waitForMe', 150); +}); +``` + +**CORRECT** + +However, the correct approach is to simply call it and let Cypress queue the task + +```ts +// feature.spec.ts +beforeEach(() => { + cy.task('waitForMe', 150); +}); +``` + +See [Cypress Docs](https://docs.cypress.io/api/commands/task#Return-a-Promise-from-an-asynchronous-task) for details + +### Setup intercepts before opening the page + +It is important that interceptors are setup before opening the page that fires the requests that are intercepted. If the interceptors are setup after the requests were made, they will not be captured and the test will timeout during `cy.wait`, + +**WRONG** + +```ts +it('calls the dependencies API', () => { + cy.visit('/app/apm/services'); + cy.intercept('GET', '/internal/apm/dependencies/top').as('topDependencies'); + cy.wait('@topDependencies'); +}); +``` + +**Correct** + +```ts +it('calls the dependencies API', () => { + cy.intercept('GET', '/internal/apm/dependencies/top').as('topDependencies'); + cy.visit('/app/apm/services'); + cy.wait('@topDependencies'); +}); +``` + +### Prefer `cy.visitKibana` instead of `cy.visit` + +In most cases we should use [`cy.visitKibana`](https://github.com/elastic/kibana/blob/50821db39c07d5d35d510c8082d5c608c4e2fd4e/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts#L51-L56) instead of `cy.visit`. +`cy.visitKibana` will wait for Kibana to have successfully loaded before moving on. This will reduce the risk of timing out later in the test because we split up the wait time in two parts: Kibana load time, and APM load time thus a time budget for each (by default 40 seconds). + +### Clean data before and after each test + +Some times test can stop in the middle of the execution and start running again, making sure that, if there were some data created, is properly cleaned before starting the test again will guarantee the proper execution of the test. + +**WRONG** + +The following will create a custom link during the test, and delete it after the test. This can lead to an invalid state if the test is stopped halfway through. + +```ts +describe('Custom links', () => { + // we check that there are not links created + it('shows empty message and create button', () => { + cy.visitKibana(basePath); + cy.contains('No links found'); + cy.contains('Create custom link'); + }); + + it('creates custom link', () => { + cy.contains('Create custom link').click(); + cy.get('input[name="label"]').type('foo'); + cy.contains('Save').click(); + cy.contains('foo'); + // if the test stops before the delete and starts again, the previous test will fail + cy.contains('Delete').click(); + }); +}); +``` + +**CORRECT** + +The correct approach is to clean up data before running the tests, preferably via api calls (as opposed to clicking the ui). + +```ts +describe('Custom links', () => { + beforeEach(() => { + cy.request({ + log: false, + method: 'DELETE', + url: `${kibanaUrl}/internal/apm/settings/custom_links/link.id`, + body: {}, + headers: { + 'kbn-xsrf': 'e2e_test', + }, + auth: { user: 'editor', pass: '****' }, + }); + }); + + it('shows empty message and create button', () => { + cy.visitKibana(basePath); + cy.contains('No links found'); + cy.contains('Create custom link'); + }); + + it('creates custom link', () => { + cy.contains('Create custom link').click(); + cy.get('input[name="label"]').type('foo'); + cy.contains('Save').click(); + cy.contains('foo'); + cy.contains('Delete').click(); + }); +}); +``` + +Use `synthtrace.clean()` after each test suit + +```ts +describe('when data is loaded', () => { + before(() => { + synthtrace.index( + generateData({ + from: new Date(start).getTime(), + to: new Date(end).getTime(), + }) + ); + }); + + after(() => { + synthtrace.clean(); + }); + + it(...) +}); +``` + ## Running tests Go to [tests documentation](../dev_docs/testing.md#e2e-tests-cypress) diff --git a/x-pack/plugins/apm/public/components/app/service_map/popover/popover.stories.tsx b/x-pack/plugins/apm/public/components/app/service_map/popover/popover.stories.tsx index 0f96b77dcf1dc..beb487aa63708 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/popover/popover.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/popover/popover.stories.tsx @@ -52,7 +52,9 @@ const stories: Meta = { return ( diff --git a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx index 50872375027dc..1a4e8a4727d85 100644 --- a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx @@ -17,7 +17,7 @@ import { } from '../../../../../../../common/agent_configuration/all_option'; import { useFetcher, FETCH_STATUS } from '../../../../../../hooks/use_fetcher'; import { FormRowSelect } from './form_row_select'; -import { APMLink } from '../../../../../shared/links/apm/apm_link'; +import { LegacyAPMLink } from '../../../../../shared/links/apm/apm_link'; import { FormRowSuggestionsSelect } from './form_row_suggestions_select'; import { SERVICE_NAME } from '../../../../../../../common/elasticsearch_fieldnames'; interface Props { @@ -142,14 +142,14 @@ export function ServicePage({ newConfig, setNewConfig, onClickNext }: Props) { {/* Cancel button */} - + {i18n.translate( 'xpack.apm.agentConfig.servicePage.cancelButton', { defaultMessage: 'Cancel' } )} - + {/* Next button */} diff --git a/x-pack/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx b/x-pack/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx index 6017d16ba2dad..82a765c0649ab 100644 --- a/x-pack/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/schema/migrated/card_footer_content.tsx @@ -9,7 +9,7 @@ import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { APMLink } from '../../../../shared/links/apm/apm_link'; +import { LegacyAPMLink } from '../../../../shared/links/apm/apm_link'; import { useFleetCloudAgentPolicyHref } from '../../../../shared/links/kibana'; export function CardFooterContent() { @@ -31,12 +31,12 @@ export function CardFooterContent() { defaultMessage="or simply return to the {serviceInventoryLink}." values={{ serviceInventoryLink: ( - + {i18n.translate( 'xpack.apm.settings.schema.success.returnText.serviceInventoryLink', { defaultMessage: 'Service inventory' } )} - + ), }} /> diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx new file mode 100644 index 0000000000000..6d0797010044d --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { IndexLifecyclePhaseSelect } from './index_lifecycle_phase_select'; +import { ServicesTable } from './services_table'; +import { SearchBar } from '../../shared/search_bar'; + +export function StorageExplorer() { + return ( + <> + + + + + + + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx new file mode 100644 index 0000000000000..6a602edc42b56 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx @@ -0,0 +1,135 @@ +/* + * Copyright 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 { EuiSuperSelect, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useHistory } from 'react-router-dom'; +import { IndexLifecyclePhaseSelectOption } from '../../../../common/storage_explorer_types'; +import * as urlHelpers from '../../shared/links/url_helpers'; +import { useApmParams } from '../../../hooks/use_apm_params'; + +export function IndexLifecyclePhaseSelect() { + const history = useHistory(); + + const { + query: { indexLifecyclePhase }, + } = useApmParams('/storage-explorer'); + + const options = [ + { + value: IndexLifecyclePhaseSelectOption.All, + label: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.all.label', + { + defaultMessage: 'All', + } + ), + description: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.all.description', + { + defaultMessage: 'Search data in all lifecycle phases.', + } + ), + }, + { + value: IndexLifecyclePhaseSelectOption.Hot, + label: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.hot.label', + { + defaultMessage: 'Hot', + } + ), + description: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.hot.description', + { + defaultMessage: + 'Holds your most-recent, most-frequently-searched data.', + } + ), + }, + { + value: IndexLifecyclePhaseSelectOption.Warm, + label: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.warm.label', + { + defaultMessage: 'Warm', + } + ), + description: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.warm.description', + { + defaultMessage: + 'Holds data from recent weeks. Updates are still allowed, but likely infrequent.', + } + ), + }, + { + value: IndexLifecyclePhaseSelectOption.Cold, + label: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.cold.label', + { + defaultMessage: 'Cold', + } + ), + description: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.cold.description', + { + defaultMessage: + 'While still searchable, this tier is typically optimized for lower storage costs rather than search speed.', + } + ), + }, + { + value: IndexLifecyclePhaseSelectOption.Frozen, + label: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.frozen.label', + { + defaultMessage: 'Frozen', + } + ), + description: i18n.translate( + 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.frozen.description', + { + defaultMessage: + 'Holds data that are no longer being queried, or being queried rarely.', + } + ), + }, + ].map(({ value, label, description }) => ({ + value, + inputDisplay: label, + dropdownDisplay: ( + <> + {label} + +

{description}

+
+ + ), + })); + + return ( + { + urlHelpers.push(history, { + query: { indexLifecyclePhase: value }, + }); + }} + hasDividers + style={{ minWidth: 200 }} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/index.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/index.tsx new file mode 100644 index 0000000000000..950996540d2f9 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/index.tsx @@ -0,0 +1,275 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, ReactNode } from 'react'; +import { + EuiInMemoryTable, + EuiBasicTableColumn, + EuiButtonIcon, + EuiScreenReaderOnly, + RIGHT_ALIGNMENT, + EuiToolTip, + EuiIcon, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ValuesType } from 'utility-types'; +import { EnvironmentBadge } from '../../../shared/environment_badge'; +import { asPercent } from '../../../../../common/utils/formatters'; +import { ServiceLink } from '../../../shared/service_link'; +import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; +import { StorageDetailsPerService } from './storage_details_per_service'; +import { getComparisonEnabled } from '../../../shared/time_comparison/get_comparison_enabled'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { asDynamicBytes } from '../../../../../common/utils/formatters'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; +import { useApmParams } from '../../../../hooks/use_apm_params'; +import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; +import { useProgressiveFetcher } from '../../../../hooks/use_progressive_fetcher'; +import { useTimeRange } from '../../../../hooks/use_time_range'; +import { SizeLabel } from './size_label'; +import type { APIReturnType } from '../../../../services/rest/create_call_apm_api'; + +type StorageExplorerItems = + APIReturnType<'GET /internal/apm/storage_explorer'>['serviceStatistics']; + +export function ServicesTable() { + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState< + Record + >({}); + + const { core } = useApmPluginContext(); + + const { + query: { + rangeFrom, + rangeTo, + environment, + kuery, + indexLifecyclePhase, + comparisonEnabled: urlComparisonEnabled, + }, + } = useApmParams('/storage-explorer'); + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + + const comparisonEnabled = getComparisonEnabled({ + core, + urlComparisonEnabled, + }); + + const toggleRowDetails = (selectedServiceName: string) => { + const expandedRowMapValues = { ...itemIdToExpandedRowMap }; + if (expandedRowMapValues[selectedServiceName]) { + delete expandedRowMapValues[selectedServiceName]; + } else { + expandedRowMapValues[selectedServiceName] = ( + + ); + } + setItemIdToExpandedRowMap(expandedRowMapValues); + }; + + const { data, status } = useProgressiveFetcher( + (callApmApi) => { + return callApmApi('GET /internal/apm/storage_explorer', { + params: { + query: { + indexLifecyclePhase, + start, + end, + environment, + kuery, + }, + }, + }); + }, + [indexLifecyclePhase, start, end, environment, kuery] + ); + + useEffect(() => { + // Closes any open rows when fetching new items + setItemIdToExpandedRowMap({}); + }, [status]); + + const loading = + status === FETCH_STATUS.NOT_INITIATED || status === FETCH_STATUS.LOADING; + + const columns: Array>> = + [ + { + field: 'serviceName', + name: i18n.translate( + 'xpack.apm.settings.storageExplorer.table.serviceColumnName', + { + defaultMessage: 'Service', + } + ), + sortable: true, + render: (_, { serviceName, agentName }) => { + const serviceLinkQuery = { + comparisonEnabled, + environment, + kuery, + rangeFrom, + rangeTo, + serviceGroup: '', + }; + + return ( + + } + /> + ); + }, + }, + { + field: 'environment', + name: i18n.translate( + 'xpack.apm.settings.storageExplorer.table.environmentColumnName', + { + defaultMessage: 'Environment', + } + ), + render: (_, { environments }) => ( + + ), + sortable: true, + }, + + { + field: 'sampling', + name: ( + + <> + {i18n.translate( + 'xpack.apm.settings.storageExplorer.table.samplingColumnName', + { + defaultMessage: 'Sample rate', + } + )}{' '} + + + + ), + render: (value: string) => asPercent(parseFloat(value), 1), + sortable: true, + }, + { + field: 'size', + name: , + render: (_, { size }) => asDynamicBytes(size) || NOT_AVAILABLE_LABEL, + sortable: true, + }, + { + align: RIGHT_ALIGNMENT, + width: '40px', + isExpander: true, + name: ( + + + {i18n.translate( + 'xpack.apm.settings.storageExplorer.table.expandRow', + { + defaultMessage: 'Expand row', + } + )} + + + ), + render: ({ serviceName }: { serviceName: string }) => { + return ( + toggleRowDetails(serviceName)} + aria-label={ + itemIdToExpandedRowMap[serviceName] + ? i18n.translate( + 'xpack.apm.settings.storageExplorer.table.collapse', + { + defaultMessage: 'Collapse', + } + ) + : i18n.translate( + 'xpack.apm.settings.storageExplorer.table.expand', + { + defaultMessage: 'Expand', + } + ) + } + iconType={ + itemIdToExpandedRowMap[serviceName] ? 'arrowUp' : 'arrowDown' + } + /> + ); + }, + }, + ]; + + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/size_label.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/size_label.tsx new file mode 100644 index 0000000000000..98a796df005e7 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/size_label.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiToolTip, EuiIcon } from '@elastic/eui'; + +export function SizeLabel() { + return ( + + <> + {i18n.translate('xpack.apm.settings.storageExplorer.sizeLabel.title', { + defaultMessage: 'Size', + })}{' '} + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx new file mode 100644 index 0000000000000..188a15ce5a972 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx @@ -0,0 +1,264 @@ +/* + * Copyright 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 { + EuiLoadingContent, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + euiPaletteColorBlind, + EuiPanel, + EuiFlexGrid, + EuiSpacer, +} from '@elastic/eui'; +import { useChartTheme } from '@kbn/observability-plugin/public'; +import { + Chart, + Partition, + Settings, + Datum, + PartitionLayout, +} from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { useEuiTheme } from '@elastic/eui'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { IndexLifecyclePhaseSelectOption } from '../../../../../common/storage_explorer_types'; +import { useApmParams } from '../../../../hooks/use_apm_params'; +import { useTimeRange } from '../../../../hooks/use_time_range'; +import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; +import { useProgressiveFetcher } from '../../../../hooks/use_progressive_fetcher'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; +import { asInteger } from '../../../../../common/utils/formatters/formatters'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; +import { asDynamicBytes } from '../../../../../common/utils/formatters'; +import { getComparisonEnabled } from '../../../shared/time_comparison/get_comparison_enabled'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { SizeLabel } from './size_label'; + +interface Props { + serviceName: string; + indexLifecyclePhase: IndexLifecyclePhaseSelectOption; +} + +const ProcessorEventLabelMap = { + [ProcessorEvent.transaction]: i18n.translate( + 'xpack.apm.settings.storageExplorer.serviceDetails.transactions', + { + defaultMessage: 'Transactions', + } + ), + [ProcessorEvent.span]: i18n.translate( + 'xpack.apm.settings.storageExplorer.serviceDetails.spans', + { + defaultMessage: 'Spans', + } + ), + [ProcessorEvent.metric]: i18n.translate( + 'xpack.apm.settings.storageExplorer.serviceDetails.metrics', + { + defaultMessage: 'Metrics', + } + ), + [ProcessorEvent.error]: i18n.translate( + 'xpack.apm.settings.storageExplorer.serviceDetails.errors', + { + defaultMessage: 'Errors', + } + ), +}; + +export function StorageDetailsPerService({ + serviceName, + indexLifecyclePhase, +}: Props) { + const { core } = useApmPluginContext(); + const chartTheme = useChartTheme(); + const router = useApmRouter(); + const { euiTheme } = useEuiTheme(); + + const { query } = useApmParams('/storage-explorer'); + const { + rangeFrom, + rangeTo, + environment, + kuery, + comparisonEnabled: urlComparisonEnabled, + } = query; + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + + const serviceOverviewLink = router.link('/services/{serviceName}/overview', { + path: { + serviceName, + }, + query: { + ...query, + serviceGroup: '', + comparisonEnabled: getComparisonEnabled({ core, urlComparisonEnabled }), + }, + }); + + const groupedPalette = euiPaletteColorBlind(); + + const { data, status } = useProgressiveFetcher( + (callApmApi) => { + return callApmApi( + 'GET /internal/apm/services/{serviceName}/storage_details', + { + params: { + path: { + serviceName, + }, + query: { + indexLifecyclePhase, + start, + end, + environment, + kuery, + }, + }, + } + ); + }, + [indexLifecyclePhase, start, end, environment, kuery, serviceName] + ); + + if ( + status === FETCH_STATUS.LOADING || + status === FETCH_STATUS.NOT_INITIATED + ) { + return ( +
+ +
+ ); + } + + if (!data || !data.processorEventStats) { + return null; + } + + const processorEventStats = data.processorEventStats.map( + ({ processorEvent, docs, size }) => ({ + processorEventLabel: ProcessorEventLabelMap[processorEvent], + docs, + size, + }) + ); + + return ( + <> + + + + + +

+ {i18n.translate( + 'xpack.apm.settings.storageExplorer.serviceDetails.title', + { + defaultMessage: 'Service storage details', + } + )} +

+
+
+ + + {i18n.translate( + 'xpack.apm.settings.storageExplorer.serviceDetails.serviceOverviewLink', + { + defaultMessage: 'Go to service overview', + } + )} + + +
+
+ + + + + + + + d.size ?? 0} + valueGetter="percent" + valueFormatter={(d: number) => + asDynamicBytes(d) || NOT_AVAILABLE_LABEL + } + layers={[ + { + groupByRollup: (d: Datum) => d.processorEventLabel, + shape: { + fillColor: (d) => groupedPalette[d.sortIndex], + }, + }, + ]} + /> + + + + + + {processorEventStats.map( + ({ processorEventLabel, docs, size }) => ( + <> + + {processorEventLabel} + + + + + + {asInteger(docs)} + {asDynamicBytes(size)} + + + + ) + )} + + + + +
+ + ); +} diff --git a/x-pack/plugins/apm/public/components/routing/home/index.tsx b/x-pack/plugins/apm/public/components/routing/home/index.tsx index 6b9a996890d9a..51a68488f9d81 100644 --- a/x-pack/plugins/apm/public/components/routing/home/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/home/index.tsx @@ -27,6 +27,7 @@ import { ApmMainTemplate } from '../templates/apm_main_template'; import { ServiceGroupTemplate } from '../templates/service_group_template'; import { dependencies } from './dependencies'; import { legacyBackends } from './legacy_backends'; +import { storageExplorer } from './storage_explorer'; export function page< TPath extends string, @@ -232,6 +233,7 @@ export const home = { }), ...dependencies, ...legacyBackends, + ...storageExplorer, '/': { element: ( diff --git a/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx b/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx new file mode 100644 index 0000000000000..b27cc2cf77bc8 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx @@ -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 React from 'react'; +import { EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import * as t from 'io-ts'; +import { StorageExplorer } from '../../app/storage_explorer'; +import { BetaBadge } from '../../shared/beta_badge'; +import { ApmMainTemplate } from '../templates/apm_main_template'; +import { Breadcrumb } from '../../app/breadcrumb'; +import { + indexLifecyclePhaseRt, + IndexLifecyclePhaseSelectOption, +} from '../../../../common/storage_explorer_types'; + +export const storageExplorer = { + '/storage-explorer': { + element: ( + + + + +

+ {i18n.translate('xpack.apm.views.storageExplorer.title', { + defaultMessage: 'Storage explorer', + })} +

+
+
+ + + +
+ } + > + + + + ), + params: t.type({ + query: indexLifecyclePhaseRt, + }), + defaults: { + query: { + indexLifecyclePhase: IndexLifecyclePhaseSelectOption.All, + }, + }, + }, +}; diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx index f073789c8ace0..d1b9e62dd5bd5 100644 --- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx @@ -42,6 +42,16 @@ export function ApmHeaderActionMenu() { return ( + + {i18n.translate('xpack.apm.storageExplorerLinkLabel', { + defaultMessage: 'Storage Explorer', + })} + { - test('APMLink should produce the correct URL', async () => { +describe('LegacyAPMLink', () => { + test('LegacyAPMLink should produce the correct URL', async () => { const href = await getRenderedHref( - () => , + () => ( + + ), { search: '?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', @@ -25,9 +27,11 @@ describe('APMLink', () => { ); }); - test('APMLink should retain current kuery value if it exists', async () => { + test('LegacyAPMLink should retain current kuery value if it exists', async () => { const href = await getRenderedHref( - () => , + () => ( + + ), { search: '?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', @@ -39,10 +43,10 @@ describe('APMLink', () => { ); }); - test('APMLink should overwrite current kuery value if new kuery value is provided', async () => { + test('LegacyAPMLink should overwrite current kuery value if new kuery value is provided', async () => { const href = await getRenderedHref( () => ( - diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx index b8eb716d15cbb..a0ba4f907cbc4 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx @@ -33,6 +33,7 @@ export const PERSISTENT_APM_PARAMS: Array = [ 'refreshInterval', 'environment', 'serviceGroup', + 'comparisonEnabled', ]; /** @@ -85,7 +86,12 @@ export function getLegacyApmHref({ }); } -export function APMLink({ path = '', query, mergeQuery, ...rest }: Props) { +export function LegacyAPMLink({ + path = '', + query, + mergeQuery, + ...rest +}: Props) { const { core } = useApmPluginContext(); const { search } = useLocation(); const { basePath } = core.http; diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/apn_link.test.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/apn_link.test.tsx deleted file mode 100644 index 7293cae85e469..0000000000000 --- a/x-pack/plugins/apm/public/components/shared/links/apm/apn_link.test.tsx +++ /dev/null @@ -1,60 +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 { Location } from 'history'; -import React from 'react'; -import { getRenderedHref } from '../../../../utils/test_helpers'; -import { APMLink } from './apm_link'; - -describe('APMLink', () => { - test('APMLink should produce the correct URL', async () => { - const href = await getRenderedHref( - () => , - { - search: - '?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', - } as Location - ); - - expect(href).toMatchInlineSnapshot( - `"/basepath/app/apm/some/path?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"` - ); - }); - - test('APMLink should retain current kuery value if it exists', async () => { - const href = await getRenderedHref( - () => , - { - search: - '?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', - } as Location - ); - - expect(href).toMatchInlineSnapshot( - `"/basepath/app/apm/some/path?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"` - ); - }); - - test('APMLink should overwrite current kuery value if new kuery value is provided', async () => { - const href = await getRenderedHref( - () => ( - - ), - { - search: - '?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', - } as Location - ); - - expect(href).toMatchInlineSnapshot( - `"/basepath/app/apm/some/path?kuery=host.os~20~3A~20~22linux~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0"` - ); - }); -}); diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx index 4bf744e29c1ee..688372f32fa34 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/error_detail_link.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './apm_link'; +import { LegacyAPMLink, APMLinkExtendProps } from './apm_link'; interface Props extends APMLinkExtendProps { serviceName: string; @@ -15,7 +15,7 @@ interface Props extends APMLinkExtendProps { function ErrorDetailLink({ serviceName, errorGroupId, ...rest }: Props) { return ( - diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/home_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/home_link.tsx index 5337f1a2f16b9..78d0d4f9fb000 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/home_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/home_link.tsx @@ -6,10 +6,10 @@ */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './apm_link'; +import { LegacyAPMLink, APMLinkExtendProps } from './apm_link'; function HomeLink(props: APMLinkExtendProps) { - return ; + return ; } export { HomeLink }; diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx index 9d9a013d1c439..5c2dd2b4b6705 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/metric_overview_link.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { APMQueryParams } from '../url_helpers'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './apm_link'; +import { LegacyAPMLink, APMLinkExtendProps, useAPMHref } from './apm_link'; const persistedFilters: Array = [ 'host', @@ -28,5 +28,5 @@ interface Props extends APMLinkExtendProps { } export function MetricOverviewLink({ serviceName, ...rest }: Props) { - return ; + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx b/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx index e169b653cffb0..7ae0440bbef3a 100644 --- a/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx @@ -16,7 +16,7 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { AnomalyDetectionSetupState } from '../../../../common/anomaly_detection/get_anomaly_detection_setup_state'; import { useMlManageJobsHref } from '../../../hooks/use_ml_manage_jobs_href'; -import { APMLink } from '../links/apm/apm_link'; +import { LegacyAPMLink } from '../links/apm/apm_link'; export function shouldDisplayMlCallout( anomalyDetectionSetupState: AnomalyDetectionSetupState @@ -58,7 +58,7 @@ export function MLCallout({ const getLearnMoreLink = (color: 'primary' | 'success') => ( - + ); diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx index 935939761a496..17753621175e9 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_toolbar.tsx @@ -16,7 +16,7 @@ import { import { i18n } from '@kbn/i18n'; import { NO_PERMISSION_LABEL } from '../../../../../common/custom_link'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { APMLink } from '../../links/apm/apm_link'; +import { LegacyAPMLink } from '../../links/apm/apm_link'; export function CustomLinkToolbar({ onClickCreate, @@ -39,13 +39,13 @@ export function CustomLinkToolbar({ defaultMessage: 'Manage custom links', })} > - + - + {showCreateButton && ( diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index abeff2b867ca6..7c9050bd3804e 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -130,6 +130,13 @@ const apmSettingsTitle = i18n.translate( } ); +const apmStorageExplorerTitle = i18n.translate( + 'xpack.apm.navigation.apmStorageExplorerTitle', + { + defaultMessage: 'Storage Explorer', + } +); + export class ApmPlugin implements Plugin { constructor( private readonly initializerContext: PluginInitializerContext @@ -311,6 +318,11 @@ export class ApmPlugin implements Plugin { path: '/dependencies/inventory', }, { id: 'settings', title: apmSettingsTitle, path: '/settings' }, + { + id: 'storage-explorer', + title: apmStorageExplorerTitle, + path: '/storage-explorer', + }, ], async mount(appMountParameters: AppMountParameters) { diff --git a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts index a7e41f86ded87..273c3952c080c 100644 --- a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts +++ b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts @@ -40,6 +40,7 @@ import { suggestionsRouteRepository } from '../suggestions/route'; import { timeRangeMetadataRoute } from '../time_range_metadata/route'; import { traceRouteRepository } from '../traces/route'; import { transactionRouteRepository } from '../transactions/route'; +import { storageExplorerRouteRepository } from '../storage_explorer/route'; function getTypedGlobalApmServerRouteRepository() { const repository = { @@ -69,6 +70,7 @@ function getTypedGlobalApmServerRouteRepository() { ...historicalDataRouteRepository, ...eventMetadataRouteRepository, ...agentKeysRouteRepository, + ...storageExplorerRouteRepository, ...spanLinksRouteRepository, ...infrastructureRouteRepository, ...debugTelemetryRoute, diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_service_statistics.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_service_statistics.ts new file mode 100644 index 0000000000000..548946288a530 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_service_statistics.ts @@ -0,0 +1,241 @@ +/* + * Copyright 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/typesWithBodyKey'; +import { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { ApmPluginRequestHandlerContext } from '../typings'; +import { Setup } from '../../lib/helpers/setup_request'; +import { + IndexLifecyclePhaseSelectOption, + indexLifeCyclePhaseToDataTier, +} from '../../../common/storage_explorer_types'; +import { getTotalTransactionsPerService } from './get_total_transactions_per_service'; +import { + PROCESSOR_EVENT, + SERVICE_NAME, + SERVICE_ENVIRONMENT, + TIER, + TRANSACTION_SAMPLED, + AGENT_NAME, + INDEX, +} from '../../../common/elasticsearch_fieldnames'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; +import { + getTotalIndicesStats, + getEstimatedSizeForDocumentsInIndex, +} from './indices_stats_helpers'; +import { RandomSampler } from '../../lib/helpers/get_random_sampler'; + +async function getMainServiceStatistics({ + setup, + context, + indexLifecyclePhase, + randomSampler, + start, + end, + environment, + kuery, +}: { + setup: Setup; + context: ApmPluginRequestHandlerContext; + indexLifecyclePhase: IndexLifecyclePhaseSelectOption; + randomSampler: RandomSampler; + start: number; + end: number; + environment: string; + kuery: string; +}) { + const { apmEventClient } = setup; + + const [allIndicesStats, response] = await Promise.all([ + getTotalIndicesStats({ context, setup }), + apmEventClient.search('get_main_service_statistics', { + apm: { + events: [ + ProcessorEvent.span, + ProcessorEvent.transaction, + ProcessorEvent.error, + ProcessorEvent.metric, + ], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + ...environmentQuery(environment), + ...kqlQuery(kuery), + ...rangeQuery(start, end), + ...(indexLifecyclePhase !== IndexLifecyclePhaseSelectOption.All + ? termQuery( + TIER, + indexLifeCyclePhaseToDataTier[indexLifecyclePhase] + ) + : []), + ] as QueryDslQueryContainer[], + }, + }, + aggs: { + sample: { + random_sampler: randomSampler, + aggs: { + services: { + terms: { + field: SERVICE_NAME, + size: 500, + }, + aggs: { + sample: { + top_metrics: { + size: 1, + metrics: { field: AGENT_NAME }, + sort: { + '@timestamp': 'desc', + }, + }, + }, + indices: { + terms: { + field: INDEX, + size: 500, + }, + aggs: { + number_of_metric_docs: { + value_count: { + field: INDEX, + }, + }, + }, + }, + environments: { + terms: { + field: SERVICE_ENVIRONMENT, + }, + }, + transactions: { + filter: { + term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction }, + }, + aggs: { + sampled_transactions: { + terms: { + field: TRANSACTION_SAMPLED, + size: 10, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }), + ]); + + const serviceStats = response.aggregations?.sample.services.buckets.map( + (bucket) => { + const estimatedSize = allIndicesStats + ? bucket.indices.buckets.reduce((prev, curr) => { + return ( + prev + + getEstimatedSizeForDocumentsInIndex({ + allIndicesStats, + indexName: curr.key as string, + numberOfDocs: curr.number_of_metric_docs.value, + }) + ); + }, 0) + : 0; + + return { + serviceName: bucket.key as string, + environments: bucket.environments.buckets.map( + ({ key }) => key as string + ), + sampledTransactionDocs: + bucket.transactions.sampled_transactions.buckets[0]?.doc_count, + size: estimatedSize, + agentName: bucket.sample.top[0]?.metrics[AGENT_NAME] as AgentName, + }; + } + ); + + return serviceStats ?? []; +} + +export async function getServiceStatistics({ + setup, + context, + indexLifecyclePhase, + randomSampler, + start, + end, + environment, + kuery, + searchAggregatedTransactions, +}: { + setup: Setup; + context: ApmPluginRequestHandlerContext; + indexLifecyclePhase: IndexLifecyclePhaseSelectOption; + randomSampler: RandomSampler; + start: number; + end: number; + environment: string; + kuery: string; + searchAggregatedTransactions: boolean; +}) { + const [docCountPerProcessorEvent, totalTransactionsPerService] = + await Promise.all([ + getMainServiceStatistics({ + setup, + context, + indexLifecyclePhase, + randomSampler, + environment, + kuery, + start, + end, + }), + getTotalTransactionsPerService({ + setup, + searchAggregatedTransactions, + indexLifecyclePhase, + randomSampler, + environment, + kuery, + start, + end, + }), + ]); + + const serviceStatistics = docCountPerProcessorEvent.map( + ({ serviceName, sampledTransactionDocs, ...rest }) => { + const sampling = + sampledTransactionDocs && totalTransactionsPerService[serviceName] + ? Math.min( + sampledTransactionDocs / totalTransactionsPerService[serviceName], + 1 + ) + : 0; + + return { + ...rest, + serviceName, + sampling, + }; + } + ); + + return serviceStatistics; +} diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_storage_details_per_processor_event.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_storage_details_per_processor_event.ts new file mode 100644 index 0000000000000..474158b41736f --- /dev/null +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_storage_details_per_processor_event.ts @@ -0,0 +1,157 @@ +/* + * Copyright 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/typesWithBodyKey'; +import { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { Setup } from '../../lib/helpers/setup_request'; +import { + PROCESSOR_EVENT, + SERVICE_NAME, + TIER, + INDEX, +} from '../../../common/elasticsearch_fieldnames'; +import { + IndexLifecyclePhaseSelectOption, + indexLifeCyclePhaseToDataTier, +} from '../../../common/storage_explorer_types'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { ApmPluginRequestHandlerContext } from '../typings'; +import { + getTotalIndicesStats, + getEstimatedSizeForDocumentsInIndex, +} from './indices_stats_helpers'; +import { RandomSampler } from '../../lib/helpers/get_random_sampler'; + +export async function getStorageDetailsPerProcessorEvent({ + setup, + context, + indexLifecyclePhase, + randomSampler, + start, + end, + environment, + kuery, + serviceName, +}: { + setup: Setup; + context: ApmPluginRequestHandlerContext; + indexLifecyclePhase: IndexLifecyclePhaseSelectOption; + randomSampler: RandomSampler; + start: number; + end: number; + environment: string; + kuery: string; + serviceName: string; +}) { + const { apmEventClient } = setup; + + const [allIndicesStats, response] = await Promise.all([ + getTotalIndicesStats({ setup, context }), + apmEventClient.search('get_storage_details_per_processor_event', { + apm: { + events: [ + ProcessorEvent.span, + ProcessorEvent.transaction, + ProcessorEvent.error, + ProcessorEvent.metric, + ], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + ...environmentQuery(environment), + ...kqlQuery(kuery), + ...rangeQuery(start, end), + ...termQuery(SERVICE_NAME, serviceName), + ...(indexLifecyclePhase !== IndexLifecyclePhaseSelectOption.All + ? termQuery( + TIER, + indexLifeCyclePhaseToDataTier[indexLifecyclePhase] + ) + : []), + ] as QueryDslQueryContainer[], + }, + }, + aggs: { + sample: { + random_sampler: randomSampler, + aggs: { + processor_event: { + terms: { + field: PROCESSOR_EVENT, + size: 10, + }, + aggs: { + number_of_metric_docs_for_processor_event: { + value_count: { + field: PROCESSOR_EVENT, + }, + }, + indices: { + terms: { + field: INDEX, + size: 500, + }, + aggs: { + number_of_metric_docs_for_index: { + value_count: { + field: INDEX, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }), + ]); + + return [ + ProcessorEvent.transaction, + ProcessorEvent.span, + ProcessorEvent.metric, + ProcessorEvent.error, + ].map((processorEvent) => { + const bucketForProcessorEvent = + response.aggregations?.sample.processor_event.buckets?.find( + (x) => x.key === processorEvent + ); + + return { + processorEvent: processorEvent as Exclude< + ProcessorEvent, + ProcessorEvent.profile + >, + docs: + bucketForProcessorEvent?.number_of_metric_docs_for_processor_event + .value ?? 0, + size: + allIndicesStats && bucketForProcessorEvent + ? bucketForProcessorEvent.indices.buckets.reduce((prev, curr) => { + return ( + prev + + getEstimatedSizeForDocumentsInIndex({ + allIndicesStats, + indexName: curr.key as string, + numberOfDocs: curr.number_of_metric_docs_for_index.value, + }) + ); + }, 0) + : 0, + }; + }); +} diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_total_transactions_per_service.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_total_transactions_per_service.ts new file mode 100644 index 0000000000000..d479c54aae2c9 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_total_transactions_per_service.ts @@ -0,0 +1,101 @@ +/* + * Copyright 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/typesWithBodyKey'; +import { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { Setup } from '../../lib/helpers/setup_request'; +import { + getProcessorEventForTransactions, + getDocumentTypeFilterForTransactions, +} from '../../lib/helpers/transactions'; +import { SERVICE_NAME, TIER } from '../../../common/elasticsearch_fieldnames'; +import { + IndexLifecyclePhaseSelectOption, + indexLifeCyclePhaseToDataTier, +} from '../../../common/storage_explorer_types'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { RandomSampler } from '../../lib/helpers/get_random_sampler'; + +export async function getTotalTransactionsPerService({ + setup, + searchAggregatedTransactions, + indexLifecyclePhase, + randomSampler, + start, + end, + environment, + kuery, +}: { + setup: Setup; + searchAggregatedTransactions: boolean; + indexLifecyclePhase: IndexLifecyclePhaseSelectOption; + randomSampler: RandomSampler; + start: number; + end: number; + environment: string; + kuery: string; +}) { + const { apmEventClient } = setup; + + const response = await apmEventClient.search( + 'get_total_transactions_per_service', + { + apm: { + events: [ + getProcessorEventForTransactions(searchAggregatedTransactions), + ], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + ...getDocumentTypeFilterForTransactions( + searchAggregatedTransactions + ), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ...rangeQuery(start, end), + ...(indexLifecyclePhase !== IndexLifecyclePhaseSelectOption.All + ? termQuery( + TIER, + indexLifeCyclePhaseToDataTier[indexLifecyclePhase] + ) + : []), + ] as QueryDslQueryContainer[], + }, + }, + aggs: { + sample: { + random_sampler: randomSampler, + aggs: { + services: { + terms: { + field: SERVICE_NAME, + size: 500, + }, + }, + }, + }, + }, + }, + } + ); + + return ( + response.aggregations?.sample.services.buckets.reduce( + (transactionsPerService, bucket) => { + transactionsPerService[bucket.key as string] = bucket.doc_count; + return transactionsPerService; + }, + {} as Record + ) ?? {} + ); +} diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/indices_stats_helpers.ts b/x-pack/plugins/apm/server/routes/storage_explorer/indices_stats_helpers.ts new file mode 100644 index 0000000000000..7abd7df856e30 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/storage_explorer/indices_stats_helpers.ts @@ -0,0 +1,48 @@ +/* + * Copyright 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 { uniq } from 'lodash'; +import { IndicesStatsIndicesStats } from '@elastic/elasticsearch/lib/api/types'; +import { Setup } from '../../lib/helpers/setup_request'; +import { ApmPluginRequestHandlerContext } from '../typings'; + +export async function getTotalIndicesStats({ + context, + setup, +}: { + context: ApmPluginRequestHandlerContext; + setup: Setup; +}) { + const { + indices: { transaction, span, metric, error }, + } = setup; + const index = uniq([transaction, span, metric, error]).join(); + const esClient = (await context.core).elasticsearch.client; + const indicesStats = (await esClient.asCurrentUser.indices.stats({ index })) + .indices; + + return indicesStats; +} + +export function getEstimatedSizeForDocumentsInIndex({ + allIndicesStats, + indexName, + numberOfDocs, +}: { + allIndicesStats: Record; + indexName: string; + numberOfDocs: number; +}) { + const indexStats = allIndicesStats[indexName]; + const indexTotalSize = indexStats?.total?.store?.size_in_bytes ?? 0; + const indexTotalDocCount = indexStats?.total?.docs?.count; + + const estimatedSize = indexTotalDocCount + ? (numberOfDocs / indexTotalDocCount) * indexTotalSize + : 0; + + return estimatedSize; +} diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/route.ts b/x-pack/plugins/apm/server/routes/storage_explorer/route.ts new file mode 100644 index 0000000000000..bcb5cb8665a38 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/storage_explorer/route.ts @@ -0,0 +1,166 @@ +/* + * Copyright 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 { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; +import { getSearchAggregatedTransactions } from '../../lib/helpers/transactions'; +import { setupRequest } from '../../lib/helpers/setup_request'; +import { indexLifecyclePhaseRt } from '../../../common/storage_explorer_types'; +import { getServiceStatistics } from './get_service_statistics'; +import { + probabilityRt, + environmentRt, + kueryRt, + rangeRt, +} from '../default_api_types'; +import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; +import { getStorageDetailsPerProcessorEvent } from './get_storage_details_per_processor_event'; +import { getRandomSampler } from '../../lib/helpers/get_random_sampler'; + +const storageExplorerRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/storage_explorer', + options: { tags: ['access:apm'] }, + params: t.type({ + query: t.intersection([ + indexLifecyclePhaseRt, + probabilityRt, + environmentRt, + kueryRt, + rangeRt, + ]), + }), + handler: async ( + resources + ): Promise<{ + serviceStatistics: Array<{ + serviceName: string; + environments: string[]; + size?: number; + agentName: AgentName; + sampling: number; + }>; + }> => { + const { + params, + context, + request, + plugins: { security }, + } = resources; + + const { + query: { + indexLifecyclePhase, + probability, + environment, + kuery, + start, + end, + }, + } = params; + + const [setup, randomSampler] = await Promise.all([ + setupRequest(resources), + getRandomSampler({ security, request, probability }), + ]); + + const searchAggregatedTransactions = await getSearchAggregatedTransactions({ + apmEventClient: setup.apmEventClient, + config: setup.config, + kuery, + }); + + const serviceStatistics = await getServiceStatistics({ + setup, + context, + indexLifecyclePhase, + randomSampler, + environment, + kuery, + start, + end, + searchAggregatedTransactions, + }); + + return { + serviceStatistics, + }; + }, +}); + +const storageExplorerServiceDetailsRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/services/{serviceName}/storage_details', + options: { tags: ['access:apm'] }, + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.intersection([ + indexLifecyclePhaseRt, + probabilityRt, + environmentRt, + kueryRt, + rangeRt, + ]), + }), + handler: async ( + resources + ): Promise<{ + processorEventStats: Array<{ + processorEvent: + | ProcessorEvent.transaction + | ProcessorEvent.error + | ProcessorEvent.metric + | ProcessorEvent.span; + docs: number; + size: number; + }>; + }> => { + const { + params, + context, + request, + plugins: { security }, + } = resources; + + const { + path: { serviceName }, + query: { + indexLifecyclePhase, + probability, + environment, + kuery, + start, + end, + }, + } = params; + + const [setup, randomSampler] = await Promise.all([ + setupRequest(resources), + getRandomSampler({ security, request, probability }), + ]); + + const processorEventStats = await getStorageDetailsPerProcessorEvent({ + setup, + context, + indexLifecyclePhase, + randomSampler, + environment, + kuery, + start, + end, + serviceName, + }); + + return { processorEventStats }; + }, +}); + +export const storageExplorerRouteRepository = { + ...storageExplorerRoute, + ...storageExplorerServiceDetailsRoute, +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx index 676d05950c227..2703424debaf7 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx @@ -12,6 +12,7 @@ import { FindingsTable } from './latest_findings_table'; import type { PropsOf } from '@elastic/eui'; import Chance from 'chance'; import type { CspFinding } from '../types'; +import type { EcsEvent } from '@kbn/logging'; import { TestProvider } from '../../../test/test_provider'; const chance = new Chance(); @@ -61,9 +62,9 @@ const getFakeFindings = (name: string): CspFinding & { id: string } => ({ sub_type: chance.string(), id: chance.string(), }, - cycle_id: chance.string(), host: {} as any, ecs: {} as any, + event: {} as EcsEvent, '@timestamp': new Date().toISOString(), }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts index 625a2c620a924..4df5e9510d292 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts @@ -7,6 +7,7 @@ import type { Criteria } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { BoolQuery, Filter, Query } from '@kbn/es-query'; +import type { EcsEvent } from '@kbn/logging'; import type { CspRuleMetadata } from '../../../common/schemas'; export type FindingsGroupByKind = 'default' | 'resource'; @@ -29,11 +30,11 @@ export interface FindingsBaseEsQuery { // TODO: this needs to be defined in a versioned schema export interface CspFinding { '@timestamp': string; - cycle_id: string; result: CspFindingResult; resource: CspFindingResource; rule: CspRuleMetadata; host: CspFindingHost; + event: EcsEvent; agent: CspFindingAgent; ecs: { version: string; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx index 181293d25c9b5..8ab26291711e9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx @@ -24,7 +24,7 @@ export const RulesBottomBar = ({ onSave, onCancel, isLoading }: RulesBottomBarPr > - + { - const { savedObjects, notifications, http } = useKibana().services; + const { notifications, http } = useKibana().services; const queryClient = useQueryClient(); return useMutation( @@ -53,20 +53,16 @@ export const useBulkUpdateCspRules = () => { }: { savedObjectRules: RuleSavedObject[]; packagePolicyId: CspRule['package_policy_id']; - }) => { - await savedObjects.client.bulkUpdate( - savedObjectRules.map((savedObjectRule) => ({ - type: CSP_RULE_SAVED_OBJECT_TYPE, - id: savedObjectRule.id, - attributes: savedObjectRule.attributes, - })) - ); - await http.post(UPDATE_RULES_CONFIG_ROUTE_PATH, { + }) => + http.post(UPDATE_RULES_CONFIG_ROUTE_PATH, { body: JSON.stringify({ package_policy_id: packagePolicyId, + rules: savedObjectRules.map((savedObjectRule) => ({ + id: savedObjectRule.id, + enabled: savedObjectRule.attributes.enabled, + })), }), - }); - }, + }), { onError: (err) => { if (err instanceof Error) notifications.toasts.addError(err, { title: UPDATE_FAILED_TEXT }); diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.ts b/x-pack/plugins/cloud_security_posture/server/plugin.ts index e287978dd0276..3ff5451e108b8 100755 --- a/x-pack/plugins/cloud_security_posture/server/plugin.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.ts @@ -37,7 +37,10 @@ import { removeCspRulesInstancesCallback, } from './fleet_integration/fleet_integration'; import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../common/constants'; -import { updateAgentConfiguration } from './routes/configuration/update_rules_configuration'; +import { + updatePackagePolicyRuntimeCfgVar, + getCspRulesSO, +} from './routes/configuration/update_rules_configuration'; import { removeFindingsStatsTask, @@ -101,15 +104,19 @@ export class CspPlugin const soClient = (await context.core).savedObjects.client; const esClient = (await context.core).elasticsearch.client.asCurrentUser; + const user = await plugins.security.authc.getCurrentUser(request); + await onPackagePolicyPostCreateCallback(this.logger, packagePolicy, soClient); - const userAuth = await plugins.security.authc.getCurrentUser(request); - const updatedPackagePolicy = await updateAgentConfiguration( - plugins.fleet.packagePolicyService, + + const updatedPackagePolicy = await updatePackagePolicyRuntimeCfgVar({ + rules: await getCspRulesSO(soClient, packagePolicy.id, packagePolicy.policy_id), packagePolicy, + packagePolicyService: plugins.fleet.packagePolicyService, esClient, soClient, - userAuth - ); + user, + }); + return updatedPackagePolicy; } diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts index 6c9d8b16f6297..b50974f2006e1 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts @@ -4,38 +4,123 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { savedObjectsClientMock, httpServiceMock, httpServerMock } from '@kbn/core/server/mocks'; +import { httpServiceMock, httpServerMock } from '@kbn/core/server/mocks'; import { createRulesConfig, defineUpdateRulesConfigRoute, - getCspRules, - setVarToPackagePolicy, - updateAgentConfiguration, + getUpdatedPackagePolicy, + updatePackagePolicyRuntimeCfgVar, + updatePackagePolicyCspRules, + updateRulesConfigurationHandler, + type UpdateRulesConfigBodySchema, + type PackagePolicyRuleUpdatePayload, + SavedObjectRuleUpdatePayload, } from './update_rules_configuration'; - import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; -import { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks'; - -import { CSP_RULE_SAVED_OBJECT_TYPE } from '../../../common/constants'; -import type { CspRule } from '../../../common/schemas'; - -import { - ElasticsearchClient, - SavedObjectsClientContract, - SavedObjectsFindResponse, -} from '@kbn/core/server'; +import type { KibanaRequest, SavedObject, SavedObjectsFindResponse } from '@kbn/core/server'; import { Chance } from 'chance'; -import { PackagePolicy, UpdatePackagePolicy } from '@kbn/fleet-plugin/common'; -import { mockAuthenticatedUser } from '@kbn/security-plugin/common/model/authenticated_user.mock'; -import { DeepPartial } from 'utility-types'; import { createCspRequestHandlerContextMock } from '../../mocks'; +import type { CspRequestHandlerContext } from '../../types'; +import type { CspRule } from '../../../common/schemas'; -describe('Update rules configuration API', () => { - let mockEsClient: jest.Mocked; - let mockSoClient: jest.Mocked; - const chance = new Chance(); +const chance = new Chance(); +/** + * rules that differ only in their 'enabled' value + */ +const setupRules = () => { + const regoRulesIds = ['cis_1_1_1', 'cis_1_1_2']; + const ids: string[] = chance.unique(chance.string, regoRulesIds.length); + const initialRulesSO: Array> = Array.from( + { length: ids.length }, + (_, i) => ({ + id: ids[i], + type: 'csp_rue', + references: [], + attributes: { + enabled: false, + metadata: { benchmark: { id: 'cis_k8s' }, rego_rule_id: regoRulesIds[i] }, + }, + }) + ); + const updatedRulesSO: Array> = initialRulesSO.map( + (rule) => ({ + ...rule, + attributes: { ...rule.attributes, enabled: true }, + }) + ); + return { updatedRulesSO, initialRulesSO }; +}; + +type CspUpdateRulesConfigMocks = Awaited>; + +const getBulkUpdateMock = ( + rules: Array> +): Array> => + rules.map((rule) => ({ + ...rule, + // Updates to rules SO only include the 'enabled' field + attributes: { enabled: rule.attributes.enabled }, + })); + +const setupRequestMock = (mocks: CspUpdateRulesConfigMocks) => { + mocks.requestMock.body = { + package_policy_id: mocks.packagePolicyMock.id, + rules: mocks.updatedRulesSO.map(({ id, attributes }) => ({ id, enabled: attributes.enabled })), + }; +}; + +const setupSoClientBulkGetMock = (mocks: CspUpdateRulesConfigMocks) => { + mocks.cspContext.soClient.bulkGet.mockReturnValue( + Promise.resolve({ saved_objects: mocks.initialRulesSO }) + ); +}; + +const setupSoClientFindMock = (mocks: CspUpdateRulesConfigMocks) => { + mocks.cspContext.soClient.find.mockReturnValue( + Promise.resolve({ + saved_objects: mocks.initialRulesSO, + total: mocks.initialRulesSO.length, + per_page: 10, + page: 1, + } as SavedObjectsFindResponse) + ); +}; + +const setupPackagePolicyServiceGetMock = (mocks: CspUpdateRulesConfigMocks) => { + mocks.packagePolicyServiceMock.get.mockReturnValue(Promise.resolve(mocks.packagePolicyMock)); +}; + +const getMocks = () => { + const { updatedRulesSO, initialRulesSO } = setupRules(); + const responseMock = httpServerMock.createResponseFactory(); + const requestMock = { + ...httpServerMock.createKibanaRequest(), + }; + const contextMock = createCspRequestHandlerContextMock(); + const packagePolicyMock = createPackagePolicyMock(); + const cspContext = contextMock.csp; + const { + soClient: soClientMock, + packagePolicyService: packagePolicyServiceMock, + esClient: esClientMock, + } = cspContext; + + return { + responseMock, + requestMock, + contextMock, + packagePolicyMock, + soClientMock, + esClientMock, + packagePolicyServiceMock, + cspContext, + initialRulesSO, + updatedRulesSO, + }; +}; + +describe('Update rules configuration API', () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -57,14 +142,13 @@ describe('Update rules configuration API', () => { const [_, handler] = router.post.mock.calls[0]; - const mockContext = createCspRequestHandlerContextMock(); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest(); - const [context, req, res] = [mockContext, mockRequest, mockResponse]; + const mocks = getMocks(); + setupRequestMock(mocks); - await handler(context, req, res); + const { contextMock, responseMock, requestMock } = mocks; + await handler(contextMock, requestMock as KibanaRequest, responseMock); - expect(res.forbidden).toHaveBeenCalledTimes(0); + expect(responseMock.forbidden).toHaveBeenCalledTimes(0); }); it('should reject to a user without fleet.all privilege', async () => { @@ -73,110 +157,34 @@ describe('Update rules configuration API', () => { defineUpdateRulesConfigRoute(router); const [_, handler] = router.post.mock.calls[0]; - const mockContext = createCspRequestHandlerContextMock(); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest(); - const [context, req, res] = [mockContext, mockRequest, mockResponse]; + const { contextMock, responseMock, requestMock } = getMocks(); - await handler(context, req, res); + contextMock.fleet = { + authz: { fleet: { all: false } }, + } as Awaited; - expect(res.forbidden).toHaveBeenCalledTimes(0); - }); + await handler(contextMock, requestMock as KibanaRequest, responseMock); - it('validate getCspRules input parameters', async () => { - const packagePolicy = createPackagePolicyMock(); - mockSoClient = savedObjectsClientMock.create(); - mockSoClient.find.mockResolvedValueOnce({} as SavedObjectsFindResponse); - await getCspRules(mockSoClient, packagePolicy); - expect(mockSoClient.find).toBeCalledTimes(1); - expect(mockSoClient.find).toHaveBeenCalledWith( - expect.objectContaining({ type: CSP_RULE_SAVED_OBJECT_TYPE }) - ); + expect(responseMock.forbidden).toHaveBeenCalledTimes(1); }); it('create csp rules config based on activated csp rules', async () => { - const cspRules: DeepPartial> = { - page: 1, - per_page: 1000, - total: 2, - saved_objects: [ - { - type: 'csp_rule', - attributes: { - enabled: true, - metadata: { - rego_rule_id: 'cis_1_1_1', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_2', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: true, - metadata: { - rego_rule_id: 'cis_1_1_3', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - ], - }; - const cspConfig = await createRulesConfig(cspRules as SavedObjectsFindResponse); + const { updatedRulesSO } = getMocks(); + + const cspConfig = createRulesConfig(updatedRulesSO); expect(cspConfig).toMatchObject({ - runtime_cfg: { activated_rules: { cis_k8s: ['cis_1_1_1', 'cis_1_1_3'] } }, + runtime_cfg: { + activated_rules: { + cis_k8s: updatedRulesSO.map((rule) => rule.attributes.metadata.rego_rule_id), + }, + }, }); }); it('create empty csp rules config when all rules are disabled', async () => { - const cspRules: DeepPartial> = { - page: 1, - per_page: 1000, - total: 2, - saved_objects: [ - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_1', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_2', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_3', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - ], - }; - const cspConfig = await createRulesConfig(cspRules as SavedObjectsFindResponse); + const { initialRulesSO } = getMocks(); + + const cspConfig = createRulesConfig(initialRulesSO); expect(cspConfig).toMatchObject({ runtime_cfg: { activated_rules: {} } }); }); @@ -185,7 +193,7 @@ describe('Update rules configuration API', () => { packagePolicy.vars = { runtimeCfg: { type: 'yaml' } }; const runtimeCfg = 'runtime_cfg:\n activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n'; - const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, runtimeCfg); + const updatedPackagePolicy = getUpdatedPackagePolicy(packagePolicy, runtimeCfg); expect(updatedPackagePolicy.vars).toEqual({ runtimeCfg: { type: 'yaml', value: runtimeCfg } }); }); @@ -193,197 +201,205 @@ describe('Update rules configuration API', () => { const packagePolicy = createPackagePolicyMock(); const runtimeCfg = 'runtime_cfg:\n activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n'; - const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, runtimeCfg); + const updatedPackagePolicy = getUpdatedPackagePolicy(packagePolicy, runtimeCfg); expect(updatedPackagePolicy.vars).toEqual({ runtimeCfg: { type: 'yaml', value: runtimeCfg } }); }); + it('updates saved-object rules', async () => { + const mocks = getMocks(); + + setupRequestMock(mocks); + setupSoClientBulkGetMock(mocks); + setupSoClientFindMock(mocks); + setupPackagePolicyServiceGetMock(mocks); + + const { updatedRulesSO, requestMock, packagePolicyMock, soClientMock, cspContext } = mocks; + + await updatePackagePolicyCspRules(cspContext, packagePolicyMock, requestMock.body.rules); + + expect(soClientMock.bulkUpdate).toBeCalledWith(getBulkUpdateMock(updatedRulesSO)); + }); + it('verify that the API for updating package policy was invoked', async () => { - mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - mockSoClient = savedObjectsClientMock.create(); - const mockPackagePolicyService = createPackagePolicyServiceMock(); - - mockPackagePolicyService.update.mockImplementation( - ( - soClient: SavedObjectsClientContract, - esClient: ElasticsearchClient, - id: string, - packagePolicyUpdate: UpdatePackagePolicy - ): Promise => { - // @ts-expect-error 2322 - return packagePolicyUpdate; - } - ); + const mocks = getMocks(); + + setupRequestMock(mocks); + setupSoClientBulkGetMock(mocks); + setupSoClientFindMock(mocks); + setupPackagePolicyServiceGetMock(mocks); + + const { esClientMock, soClientMock, packagePolicyMock, packagePolicyServiceMock, cspContext } = + mocks; + packagePolicyServiceMock.update.mockReturnValue(Promise.resolve(packagePolicyMock)); + + const updatedPackagePolicy = await updatePackagePolicyRuntimeCfgVar({ + rules: mocks.updatedRulesSO, + packagePolicy: packagePolicyMock, + soClient: soClientMock, + esClient: esClientMock.asCurrentUser, + packagePolicyService: packagePolicyServiceMock, + user: cspContext.user, + }); - const cspRules: DeepPartial> = { - page: 1, - per_page: 1000, - total: 2, - saved_objects: [ - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_1', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_2', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_3', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - ], - }; - mockSoClient.find.mockResolvedValueOnce(cspRules as SavedObjectsFindResponse); - - const mockPackagePolicy = createPackagePolicyMock(); - mockPackagePolicy.vars = { runtimeCfg: { type: 'foo' } }; - const packagePolicyId1 = chance.guid(); - mockPackagePolicy.id = packagePolicyId1; - - const user = null; - - const updatePackagePolicy = await updateAgentConfiguration( - mockPackagePolicyService, - mockPackagePolicy, - mockEsClient, - mockSoClient, - user + expect(packagePolicyServiceMock.update).toHaveBeenCalled(); + const { id, ...updatedPackagePolicyWithoutRevisionId } = updatedPackagePolicy; + expect(packagePolicyServiceMock.update.mock.calls[0][3]).toMatchObject( + updatedPackagePolicyWithoutRevisionId ); + }); + + it("updates to package policy vars doesn't override unknown vars", async () => { + const mocks = getMocks(); + + setupRequestMock(mocks); + setupSoClientBulkGetMock(mocks); + setupSoClientFindMock(mocks); + setupPackagePolicyServiceGetMock(mocks); + + const { esClientMock, soClientMock, packagePolicyMock, packagePolicyServiceMock, cspContext } = + mocks; + + const dummyVar = { type: 'ymal', value: 'foo ' }; + packagePolicyMock.vars = { runtimeCfg: { type: 'yaml' }, foo: { ...dummyVar } }; + packagePolicyServiceMock.update.mockReturnValue(Promise.resolve(packagePolicyMock)); + + const updatedPackagePolicy = await updatePackagePolicyRuntimeCfgVar({ + rules: mocks.updatedRulesSO, + packagePolicy: packagePolicyMock, + soClient: soClientMock, + esClient: esClientMock.asCurrentUser, + packagePolicyService: packagePolicyServiceMock, + user: cspContext.user, + }); + + expect(updatedPackagePolicy.vars!.foo).toEqual(dummyVar); + expect(updatedPackagePolicy.vars!.runtimeCfg).toBeDefined(); + }); - expect(updatePackagePolicy.vars!.runtimeCfg).toHaveProperty('value'); - expect(updatePackagePolicy.vars!.runtimeCfg).toMatchObject({ type: 'yaml' }); - expect(mockPackagePolicyService.update).toBeCalledTimes(1); - expect(mockPackagePolicyService.update.mock.calls[0][2]).toEqual(packagePolicyId1); + it('attempts to rollback rules saved-object when package policy update failed', async () => { + const mocks = getMocks(); + + setupRequestMock(mocks); + setupSoClientBulkGetMock(mocks); + setupSoClientFindMock(mocks); + setupPackagePolicyServiceGetMock(mocks); + + const { + updatedRulesSO, + initialRulesSO, + requestMock, + soClientMock, + packagePolicyMock, + packagePolicyServiceMock, + cspContext, + } = mocks; + + packagePolicyServiceMock.update.mockReturnValue(Promise.reject('some error')); + + try { + await updatePackagePolicyCspRules(cspContext, packagePolicyMock, requestMock.body.rules); + } catch (e) { + expect(soClientMock.bulkUpdate).toHaveBeenNthCalledWith(1, getBulkUpdateMock(updatedRulesSO)); + expect(soClientMock.bulkUpdate).toHaveBeenNthCalledWith(2, initialRulesSO); + } }); - it('validate updateAgentConfiguration not override vars', async () => { - mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - mockSoClient = savedObjectsClientMock.create(); - const mockPackagePolicyService = createPackagePolicyServiceMock(); + it('updates package policy as authenticated user', async () => { + const mocks = getMocks(); - const cspRules: DeepPartial> = { - page: 1, - per_page: 1000, - total: 2, - saved_objects: [ - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_1', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_2', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_3', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - ], - }; - mockSoClient.find.mockResolvedValueOnce(cspRules as SavedObjectsFindResponse); - - const mockPackagePolicy = createPackagePolicyMock(); - const packagePolicyId1 = chance.guid(); - const user = null; - mockPackagePolicy.id = packagePolicyId1; - mockPackagePolicy.vars = { foo: {}, runtimeCfg: { type: 'yaml' } }; - - mockPackagePolicyService.update.mockImplementation( - ( - soClient: SavedObjectsClientContract, - esClient: ElasticsearchClient, - id: string, - packagePolicyUpdate: UpdatePackagePolicy - ): Promise => { - // @ts-expect-error 2322 - return packagePolicyUpdate; - } - ); + setupRequestMock(mocks); + setupSoClientBulkGetMock(mocks); + setupSoClientFindMock(mocks); + setupPackagePolicyServiceGetMock(mocks); - const updatedPackagePolicy = await updateAgentConfiguration( - mockPackagePolicyService, - mockPackagePolicy, - mockEsClient, - mockSoClient, - user - ); + const { requestMock, packagePolicyMock, packagePolicyServiceMock, cspContext } = mocks; + + packagePolicyServiceMock.update.mockReturnValue(Promise.resolve(packagePolicyMock)); - expect(mockPackagePolicyService.update).toBeCalledTimes(1); - expect(updatedPackagePolicy.vars).toHaveProperty('foo'); + await updatePackagePolicyCspRules(cspContext, packagePolicyMock, requestMock.body.rules); + + expect(packagePolicyServiceMock.update.mock.calls[0][4]).toMatchObject({ + user: cspContext.user, + }); }); - it('validate updateAgentConfiguration passes user to the package update method', async () => { - mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - mockSoClient = savedObjectsClientMock.create(); + it('handles failed rules saved object update', async () => { + const mocks = getMocks(); + + setupRequestMock(mocks); + setupSoClientBulkGetMock(mocks); + setupSoClientFindMock(mocks); + setupPackagePolicyServiceGetMock(mocks); + + const { requestMock, responseMock, soClientMock, contextMock, packagePolicyServiceMock } = + mocks; + + const message = 'some error'; + soClientMock.bulkUpdate.mockReturnValue(Promise.reject(message)); + + try { + await updateRulesConfigurationHandler( + contextMock as unknown as CspRequestHandlerContext, + requestMock, + responseMock + ); + } catch (e) { + expect(packagePolicyServiceMock.update).not.toHaveBeenCalled(); + expect(responseMock.customError).toHaveBeenCalledWith( + expect.objectContaining({ + message, + }) + ); + } + }); - const mockPackagePolicyService = createPackagePolicyServiceMock(); - const mockPackagePolicy = createPackagePolicyMock(); - const user = mockAuthenticatedUser(); + it('handles failed rules package policy update', async () => { + const mocks = getMocks(); + + setupRequestMock(mocks); + setupSoClientBulkGetMock(mocks); + setupSoClientFindMock(mocks); + setupPackagePolicyServiceGetMock(mocks); + + const { requestMock, responseMock, packagePolicyServiceMock, contextMock } = mocks; + + const message = 'some error'; + packagePolicyServiceMock.update.mockReturnValue(Promise.reject(message)); + + try { + await updateRulesConfigurationHandler( + contextMock as unknown as CspRequestHandlerContext, + requestMock, + responseMock + ); + } catch (e) { + expect(responseMock.customError).toHaveBeenCalledWith( + expect.objectContaining({ + message, + }) + ); + } + }); - const cspRules: DeepPartial> = { - page: 1, - per_page: 1000, - total: 2, - saved_objects: [ - { - type: 'csp_rule', - attributes: { - enabled: false, - metadata: { - rego_rule_id: 'cis_1_1_1', - benchmark: { id: 'cis_k8s' }, - }, - }, - }, - ], - }; - mockSoClient.find.mockResolvedValueOnce(cspRules as SavedObjectsFindResponse); - - mockPackagePolicy.vars = { runtimeCfg: { type: 'yaml' } }; - - await updateAgentConfiguration( - mockPackagePolicyService, - mockPackagePolicy, - mockEsClient, - mockSoClient, - user - ); - expect(mockPackagePolicyService.update.mock.calls[0][4]).toMatchObject({ user }); + it('throws a 404 Saved-Object Error when packagePolicyService returns a null', async () => { + const mocks = getMocks(); + setupRequestMock(mocks); + + mocks.packagePolicyServiceMock.get.mockReturnValue(Promise.resolve(null)); + const { requestMock, responseMock, contextMock } = mocks; + + try { + await updateRulesConfigurationHandler( + contextMock as unknown as CspRequestHandlerContext, + requestMock, + responseMock + ); + } catch (e) { + expect(responseMock.customError).toHaveBeenCalledWith( + expect.objectContaining({ + statusCode: 404, + }) + ); + } }); }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts index 6c6358e772e6a..8467b18de27cc 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts @@ -4,68 +4,65 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { - ElasticsearchClient, - SavedObjectsClientContract, - SavedObjectsFindResponse, +import { schema as rt, TypeOf } from '@kbn/config-schema'; +import yaml from 'js-yaml'; +import { type PackagePolicy, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; +import { + type ElasticsearchClient, + type SavedObject, + type Logger, + type SavedObjectsClientContract, + SavedObjectsErrorHelpers, } from '@kbn/core/server'; -import { schema as rt } from '@kbn/config-schema'; import { transformError } from '@kbn/securitysolution-es-utils'; - -import { produce } from 'immer'; +import type { PackagePolicyServiceInterface } from '@kbn/fleet-plugin/server'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { unset } from 'lodash'; -import yaml from 'js-yaml'; - -import { PackagePolicy, PackagePolicyConfigRecord } from '@kbn/fleet-plugin/common'; -import { PackagePolicyServiceInterface } from '@kbn/fleet-plugin/server'; -import { AuthenticatedUser } from '@kbn/security-plugin/common'; +import produce from 'immer'; import { createCspRuleSearchFilterByPackagePolicy } from '../../../common/utils/helpers'; - import type { CspRule, CspRulesConfiguration } from '../../../common/schemas'; import { - CLOUD_SECURITY_POSTURE_PACKAGE_NAME, CSP_RULE_SAVED_OBJECT_TYPE, UPDATE_RULES_CONFIG_ROUTE_PATH, } from '../../../common/constants'; -import { CspRouter } from '../../types'; +import type { CspApiRequestHandlerContext, CspRouter, CspRequestHandler } from '../../types'; -export const getPackagePolicy = async ( - soClient: SavedObjectsClientContract, - packagePolicyService: PackagePolicyServiceInterface, - packagePolicyId: string -): Promise => { - const packagePolicies = await packagePolicyService.getByIDs(soClient, [packagePolicyId]); +export type UpdateRulesConfigBodySchema = TypeOf; - // PackagePolicies always contains one element, even when package does not exist - if (!packagePolicies || !packagePolicies[0].version) { - throw new Error(`Package policy Id '${packagePolicyId}' does not exist`); - } - if (packagePolicies[0].package?.name !== CLOUD_SECURITY_POSTURE_PACKAGE_NAME) { - throw new Error( - `Package Policy Id '${packagePolicyId}' is not of type cloud security posture package` - ); - } +/** Minimal set of fields required for updating a CSP SO rule */ +export type SavedObjectRuleUpdatePayload = Pick; - return packagePolicies![0]; -}; +/** + * Minimal set of fields required for generating + * the value of the runtime config key on the package policy vars object + */ +export interface PackagePolicyRuleUpdatePayload { + enabled: boolean; + metadata: { benchmark: { id: string }; rego_rule_id: string }; +} -export const getCspRules = ( - soClient: SavedObjectsClientContract, - packagePolicy: PackagePolicy -): Promise> => { - return soClient.find({ - type: CSP_RULE_SAVED_OBJECT_TYPE, - filter: createCspRuleSearchFilterByPackagePolicy({ - packagePolicyId: packagePolicy.id, - policyId: packagePolicy.policy_id, - }), - searchFields: ['name'], - // TODO: research how to get all rules - perPage: 10000, - }); -}; +const RUNTIME_CFG_FIELDS = ['enabled', 'metadata.benchmark.id', 'metadata.rego_rule_id']; -const getEnabledRulesByBenchmark = (rules: SavedObjectsFindResponse['saved_objects']) => +const updateRulesConfigurationBodySchema = rt.object({ + /** + * CSP integration instance ID + */ + package_policy_id: rt.string(), + /** + * CSP rules to update + */ + rules: rt.arrayOf( + rt.object({ + /** + * the rule saved-object id + */ + id: rt.string(), + enabled: rt.boolean(), + }) + ), +}); + +const getEnabledRulesByBenchmark = (rules: Array>) => rules.reduce((benchmarks, rule) => { const benchmark = rule.attributes.metadata.benchmark.id; if (!rule.attributes.enabled || !benchmark) return benchmarks; @@ -77,55 +74,246 @@ const getEnabledRulesByBenchmark = (rules: SavedObjectsFindResponse['sa return benchmarks; }, {}); +/* @internal */ export const createRulesConfig = ( - cspRules: SavedObjectsFindResponse + cspRules: Array> ): CspRulesConfiguration => ({ runtime_cfg: { - activated_rules: getEnabledRulesByBenchmark(cspRules.saved_objects), + activated_rules: getEnabledRulesByBenchmark(cspRules), }, }); -export const convertRulesConfigToYaml = (config: CspRulesConfiguration): string => { - return yaml.safeDump(config); -}; - -export const setVarToPackagePolicy = ( +/* @internal */ +export const getUpdatedPackagePolicy = ( packagePolicy: PackagePolicy, runtimeCfg: string -): PackagePolicy => { - const configFile: PackagePolicyConfigRecord = { - runtimeCfg: { type: 'yaml', value: runtimeCfg }, - }; - const updatedPackagePolicy = produce(packagePolicy, (draft) => { +): PackagePolicy => + produce(packagePolicy, (draft) => { unset(draft, 'id'); - if (draft.vars) { - draft.vars.runtimeCfg = configFile.runtimeCfg; - } else { - draft.vars = configFile; - } + + if (!draft.vars) draft.vars = {}; + draft.vars.runtimeCfg = { type: 'yaml', value: runtimeCfg }; }); - return updatedPackagePolicy; -}; -export const updateAgentConfiguration = async ( - packagePolicyService: PackagePolicyServiceInterface, - packagePolicy: PackagePolicy, - esClient: ElasticsearchClient, +/** + * gets all rules of a package policy with fields required for runtime config + */ +export const getCspRulesSO = ( soClient: SavedObjectsClientContract, - user: AuthenticatedUser | null -): Promise => { - const cspRules = await getCspRules(soClient, packagePolicy); - const rulesConfig = createRulesConfig(cspRules); - const runtimeCfg = convertRulesConfigToYaml(rulesConfig); - const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, runtimeCfg); - const options = { user: user ? user : undefined }; - return packagePolicyService.update( + packagePolicyId: PackagePolicy['id'], + policyId: PackagePolicy['policy_id'] +): Promise>> => + soClient + .find({ + type: CSP_RULE_SAVED_OBJECT_TYPE, + filter: createCspRuleSearchFilterByPackagePolicy({ + packagePolicyId, + policyId, + }), + fields: RUNTIME_CFG_FIELDS, + perPage: 10000, + }) + .then((response) => response.saved_objects); + +/** + * gets rules of a package policy by id with fields required for updating SO object + */ +const getByIdCurrentRulesSO = ( + soClient: CspApiRequestHandlerContext['soClient'], + ruleIds: string[] +): Promise>> => + soClient + .bulkGet( + ruleIds.map((ruleId) => ({ + id: ruleId, + type: CSP_RULE_SAVED_OBJECT_TYPE, + fields: ['enabled'], // In case of a rollback, this is the only field we need to restore + })) + ) + .then((response) => response.saved_objects); + +const getRuntimeCfgVarValue = (rules: Array>) => + yaml.safeDump(createRulesConfig(rules)); + +/** + * Updates the package policy vars object with a new value for the runtimeCfg key + * @internal + * */ +export const updatePackagePolicyRuntimeCfgVar = async ({ + packagePolicyService, + packagePolicy, + esClient, + soClient, + user, + rules, +}: { + packagePolicyService: PackagePolicyServiceInterface; + packagePolicy: PackagePolicy; + esClient: ElasticsearchClient; + soClient: SavedObjectsClientContract; + user: AuthenticatedUser | null; + rules: Array>; +}): Promise => + packagePolicyService.update( soClient, esClient, packagePolicy.id, - updatedPackagePolicy, - options + getUpdatedPackagePolicy(packagePolicy, getRuntimeCfgVarValue(rules)), + user ? { user } : undefined + ); + +/** + * Keeps only fields we want/need to use for updating a rule saved-object + */ +const getRuleUpdateFields = ({ + id, + type, + attributes, + references, +}: SavedObject) => ({ id, type, attributes, references }); + +/** + * Merges the current rules SO with their new 'enabled' value + */ +const getNextCspRulesSO = ( + current: Array>, + rulesToChange: UpdateRulesConfigBodySchema['rules'] +): Array> => { + const currentRulesMap = Object.fromEntries( + current.map((rule) => [rule.id, getRuleUpdateFields(rule)]) ); + + return rulesToChange.map((rule) => ({ + ...currentRulesMap[rule.id], + attributes: { enabled: rule.enabled }, + })); +}; + +const updateRulesSO = >( + soClient: SavedObjectsClientContract, + rulesSO: Array> +) => soClient.bulkUpdate(rulesSO); + +const runUpdate = async ({ + rollbackSO, + updateSO, + updatePackagePolicy, + logger, +}: { + rollbackSO: () => Promise; + updateSO: () => Promise; + updatePackagePolicy: () => Promise; + logger: Logger; +}): Promise => { + logger.info('Start updating rules'); + + try { + await updateSO(); + } catch (e) { + logger.warn('Failed updating saved objects'); + logger.error(e); + throw e; + } + + try { + const updatedPolicy = await updatePackagePolicy(); + logger.info('Finish updating rules'); + return updatedPolicy; + } catch (e) { + logger.warn('Failed updating package policy vars'); + logger.error(e); + + logger.info('Rollback to previous saved-objects rules'); + await rollbackSO(); + logger.info('Finish rollback'); + + throw e; + } +}; +/** + * Combines updating rules saved-objects and package policy vars into a single operation + * @internal + */ +export const updatePackagePolicyCspRules = async ( + { soClient, packagePolicyService, esClient, user, logger }: CspApiRequestHandlerContext, + packagePolicy: PackagePolicy, + rulesToChange: UpdateRulesConfigBodySchema['rules'] +): Promise => { + // Get initial rules, before updating + const currentRulesSO = await getByIdCurrentRulesSO( + soClient, + rulesToChange.map((rule) => rule.id) + ); + + // Define actions to be executed for updating rules + const updateSO = () => updateRulesSO(soClient, getNextCspRulesSO(currentRulesSO, rulesToChange)); + const updatePackagePolicy = async () => + updatePackagePolicyRuntimeCfgVar({ + rules: await getCspRulesSO(soClient, packagePolicy.id, packagePolicy.policy_id), + soClient, + packagePolicyService, + user, + packagePolicy, + esClient: esClient.asCurrentUser, + }); + + // Define actions to be executed for rollback + const rollbackSO = () => updateRulesSO(soClient, currentRulesSO); + + // Start the update process + return runUpdate({ + logger, + updateSO, + updatePackagePolicy, + rollbackSO, + }); +}; + +/** @internal */ +export const updateRulesConfigurationHandler: CspRequestHandler< + void, + void, + UpdateRulesConfigBodySchema +> = async (context, request, response) => { + const cspContext = await context.csp; + + if (!(await context.fleet).authz.fleet.all) { + return response.forbidden(); + } + + try { + const packagePolicy = await cspContext.packagePolicyService.get( + cspContext.soClient, + request.body.package_policy_id + ); + if (!packagePolicy) { + // packagePolicyService.get() throws a 404 if the package policy is not found + // we consider returning null as the same, and use the same error + throw SavedObjectsErrorHelpers.createGenericNotFoundError( + PACKAGE_POLICY_SAVED_OBJECT_TYPE, + request.body.package_policy_id + ); + } + + cspContext.logger.info(`Start updating package policy - ${packagePolicy.id}`); + + const updatedPackagePolicy = await updatePackagePolicyCspRules( + cspContext, + packagePolicy, + request.body.rules + ); + return response.ok({ body: updatedPackagePolicy }); + } catch (e) { + const error = transformError(e); + cspContext.logger.error( + `Failed updating package policy (${request.body.package_policy_id}) - ${error.message}` + ); + + return response.customError({ + body: { message: error.message }, + statusCode: error.statusCode, + }); + } }; export const defineUpdateRulesConfigRoute = (router: CspRouter): void => @@ -137,45 +325,5 @@ export const defineUpdateRulesConfigRoute = (router: CspRouter): void => tags: ['access:cloud-security-posture-all'], }, }, - async (context, request, response) => { - const cspContext = await context.csp; - - if (!(await context.fleet).authz.fleet.all) { - return response.forbidden(); - } - - try { - const packagePolicy = await getPackagePolicy( - cspContext.soClient, - cspContext.packagePolicyService, - request.body.package_policy_id - ); - - const updatedPackagePolicy = await updateAgentConfiguration( - cspContext.packagePolicyService, - packagePolicy, - cspContext.esClient.asCurrentUser, - cspContext.soClient, - cspContext.user - ); - - return response.ok({ body: updatedPackagePolicy }); - } catch (err) { - const error = transformError(err); - cspContext.logger.error( - `Failed to update rules configuration on package policy ${error.message}` - ); - return response.customError({ - body: { message: error.message }, - statusCode: error.statusCode, - }); - } - } + updateRulesConfigurationHandler ); - -export const updateRulesConfigurationBodySchema = rt.object({ - /** - * CSP integration instance ID - */ - package_policy_id: rt.string(), -}); diff --git a/x-pack/plugins/cloud_security_posture/server/types.ts b/x-pack/plugins/cloud_security_posture/server/types.ts index 43f47585d09f6..37a0c90a24a1c 100644 --- a/x-pack/plugins/cloud_security_posture/server/types.ts +++ b/x-pack/plugins/cloud_security_posture/server/types.ts @@ -19,6 +19,9 @@ import type { Logger, SavedObjectsClientContract, IScopedClusterClient, + KibanaResponseFactory, + RequestHandler, + RouteMethod, } from '@kbn/core/server'; import type { AgentService, @@ -70,6 +73,18 @@ export type CspRequestHandlerContext = CustomRequestHandlerContext<{ fleet: FleetRequestHandlerContext['fleet']; }>; +/** + * Convenience type for request handlers in CSP that includes the CspRequestHandlerContext type + * @internal + */ +export type CspRequestHandler< + P = unknown, + Q = unknown, + B = unknown, + Method extends RouteMethod = any, + ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory +> = RequestHandler; + /** * Convenience type for routers in Csp that includes the CspRequestHandlerContext type * @internal diff --git a/x-pack/plugins/enterprise_search/common/types/connectors.ts b/x-pack/plugins/enterprise_search/common/types/connectors.ts index 3c986d4ac1143..26b7ef917f435 100644 --- a/x-pack/plugins/enterprise_search/common/types/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/types/connectors.ts @@ -39,7 +39,7 @@ export interface Connector { language: string | null; last_seen: string | null; last_sync_error: string | null; - last_sync_status: string | null; + last_sync_status: SyncStatus | null; last_synced: string | null; name: string; scheduling: { @@ -52,3 +52,16 @@ export interface Connector { } export type ConnectorDocument = Omit; + +export interface ConnectorSyncJob { + completed_at: string | null; + connector?: ConnectorDocument; + connector_id: string; + created_at: string; + deleted_document_count: number; + error: string | null; + index_name: string; + indexed_document_count: number; + status: SyncStatus; + worker_hostname: string; +} diff --git a/x-pack/plugins/enterprise_search/common/types/pagination.ts b/x-pack/plugins/enterprise_search/common/types/pagination.ts new file mode 100644 index 0000000000000..04f0fa6a46bc7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/common/types/pagination.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. + */ + +export interface Paginate { + data: T[]; + has_more_hits_than_total: boolean; + pageIndex: number; + pageSize: number; + size: number; + total: number; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.test.ts new file mode 100644 index 0000000000000..eadad25b8342a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.test.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. + */ + +import { mockHttpValues } from '../../../__mocks__/kea_logic'; + +import { nextTick } from '@kbn/test-jest-helpers'; + +import { fetchSyncJobs } from './fetch_sync_jobs_api_logic'; + +describe('FetchSyncJobs', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('fetchSyncJobs', () => { + it('calls correct api', async () => { + const promise = Promise.resolve('result'); + http.get.mockReturnValue(promise); + const result = fetchSyncJobs({ connectorId: 'connectorId1' }); + await nextTick(); + expect(http.get).toHaveBeenCalledWith( + '/internal/enterprise_search/connectors/connectorId1/sync_jobs', + { query: { page: 0, size: 10 } } + ); + await expect(result).resolves.toEqual('result'); + }); + it('appends query if specified', async () => { + const promise = Promise.resolve('result'); + http.get.mockReturnValue(promise); + const result = fetchSyncJobs({ connectorId: 'connectorId1', page: 10, size: 20 }); + await nextTick(); + expect(http.get).toHaveBeenCalledWith( + '/internal/enterprise_search/connectors/connectorId1/sync_jobs', + { query: { page: 10, size: 20 } } + ); + await expect(result).resolves.toEqual('result'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.ts new file mode 100644 index 0000000000000..a45bf5e0943ca --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.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 { ConnectorSyncJob } from '../../../../../common/types/connectors'; +import { Paginate } from '../../../../../common/types/pagination'; + +import { createApiLogic } from '../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../shared/http'; + +export interface FetchSyncJobsArgs { + connectorId: string; + page?: number; + size?: number; +} + +export type FetchSyncJobsResponse = Paginate; + +export const fetchSyncJobs = async ({ connectorId, page = 0, size = 10 }: FetchSyncJobsArgs) => { + const route = `/internal/enterprise_search/connectors/${connectorId}/sync_jobs`; + const query = { page, size }; + return await HttpLogic.values.http.get>(route, { query }); +}; + +export const FetchSyncJobsApiLogic = createApiLogic( + ['enterprise_search_content', 'fetch_sync_api_logic'], + fetchSyncJobs +); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts index 36a3e5b664ac9..e09b66051e4aa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts @@ -26,6 +26,7 @@ import { IndexViewLogic } from './index_view_logic'; // We can't test fetchTimeOutId because this will get set whenever the logic is created // And the timeoutId is non-deterministic. We use expect.object.containing throughout this test file const DEFAULT_VALUES = { + connectorId: null, data: undefined, index: undefined, indexName: '', @@ -41,6 +42,7 @@ const DEFAULT_VALUES = { const CONNECTOR_VALUES = { ...DEFAULT_VALUES, + connectorId: connectorIndex.connector.id, data: connectorIndex, index: indexToViewIndex(connectorIndex), ingestionMethod: IngestionMethod.CONNECTOR, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts index 4ed88ffe7ba8c..e49745dacfcb7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts @@ -29,6 +29,7 @@ import { getLastUpdated, indexToViewIndex, isConnectorIndex, + isConnectorViewIndex, isCrawlerIndex, } from '../../utils/indices'; @@ -61,6 +62,7 @@ export interface IndexViewActions { } export interface IndexViewValues { + connectorId: string | null; data: typeof FetchIndexApiLogic.values.data; fetchIndexTimeoutId: NodeJS.Timeout | null; index: ElasticsearchViewIndex | undefined; @@ -170,7 +172,7 @@ export const IndexViewLogic = kea { if (isConnectorIndex(values.data)) { - actions.makeStartSyncRequest({ connectorId: values.data?.connector?.id }); + actions.makeStartSyncRequest({ connectorId: values.data.connector.id }); } }, startSyncApiError: (e) => flashAPIErrors(e), @@ -215,6 +217,10 @@ export const IndexViewLogic = kea ({ + connectorId: [ + () => [selectors.index], + (index) => (isConnectorViewIndex(index) ? index.connector.id : null), + ], index: [() => [selectors.data], (data) => (data ? indexToViewIndex(data) : undefined)], ingestionMethod: [() => [selectors.data], (data) => getIngestionMethod(data)], ingestionStatus: [() => [selectors.data], (data) => getIngestionStatus(data)], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx index bcbc2847ff3e1..c0b392b646626 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx @@ -21,6 +21,7 @@ import { CrawlRequestsPanel } from './crawler/crawl_requests_panel/crawl_request import { CrawlerTotalStats } from './crawler_total_stats'; import { GenerateApiKeyPanel } from './generate_api_key_panel'; import { OverviewLogic } from './overview.logic'; +import { SyncJobs } from './sync_jobs'; import { TotalStats } from './total_stats'; export const SearchIndexOverview: React.FC = () => { @@ -67,6 +68,8 @@ export const SearchIndexOverview: React.FC = () => { <> + + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs.tsx new file mode 100644 index 0000000000000..708f8a6a77253 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { EuiBadge, EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { SyncStatus } from '../../../../../common/types/connectors'; + +import { FormattedDateTime } from '../../../shared/formatted_date_time'; +import { durationToText } from '../../utils/duration_to_text'; + +import { syncStatusToColor, syncStatusToText } from '../../utils/sync_status_to_text'; + +import { IndexViewLogic } from './index_view_logic'; +import { SyncJobsViewLogic, SyncJobView } from './sync_jobs_view_logic'; + +export const SyncJobs: React.FC = () => { + const { connectorId } = useValues(IndexViewLogic); + const { syncJobs, syncJobsLoading, syncJobsPagination } = useValues(SyncJobsViewLogic); + const { fetchSyncJobs } = useActions(SyncJobsViewLogic); + + useEffect(() => { + if (connectorId) { + fetchSyncJobs({ + connectorId, + page: syncJobsPagination.pageIndex ?? 0, + size: syncJobsPagination.pageSize ?? 10, + }); + } + }, [connectorId]); + + const columns: Array> = [ + { + field: 'lastSync', + name: i18n.translate('xpack.enterpriseSearch.content.syncJobs.lastSync.columnTitle', { + defaultMessage: 'Last sync', + }), + render: (lastSync: string) => , + sortable: true, + truncateText: true, + width: '25%', + }, + { + field: 'duration', + name: i18n.translate('xpack.enterpriseSearch.content.syncJobs.syncDuration.columnTitle', { + defaultMessage: 'Sync duration', + }), + render: (duration: moment.Duration) => durationToText(duration), + sortable: true, + truncateText: true, + width: '25%', + }, + { + field: 'docsCount', + name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.docsCount.columnTitle', { + defaultMessage: 'Docs count', + }), + sortable: true, + truncateText: true, + width: '25%', + }, + { + field: 'status', + name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.syncStatus.columnTitle', { + defaultMessage: 'Status', + }), + render: (syncStatus: SyncStatus) => ( + {syncStatusToText(syncStatus)} + ), + truncateText: true, + width: '25%', + }, + ]; + + return ( + { + if (connectorId) { + fetchSyncJobs({ connectorId, page: index, size: syncJobsPagination.pageSize }); + } + }} + pagination={{ + ...syncJobsPagination, + totalItemCount: syncJobsPagination.total, + }} + tableLayout="fixed" + loading={syncJobsLoading} + /> + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs_view_logic.test.ts new file mode 100644 index 0000000000000..52526abbfcef4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs_view_logic.test.ts @@ -0,0 +1,181 @@ +/* + * Copyright 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 { LogicMounter, mockFlashMessageHelpers } from '../../../__mocks__/kea_logic'; + +import moment from 'moment'; + +import { nextTick } from '@kbn/test-jest-helpers'; + +import { HttpError, Status } from '../../../../../common/types/api'; + +import { SyncStatus } from '../../../../../common/types/connectors'; +import { FetchSyncJobsApiLogic } from '../../api/connector/fetch_sync_jobs_api_logic'; + +import { IndexViewLogic } from './index_view_logic'; +import { SyncJobView, SyncJobsViewLogic } from './sync_jobs_view_logic'; + +// We can't test fetchTimeOutId because this will get set whenever the logic is created +// And the timeoutId is non-deterministic. We use expect.object.containing throughout this test file +const DEFAULT_VALUES = { + connectorId: null, + syncJobs: [], + syncJobsData: undefined, + syncJobsLoading: true, + syncJobsPagination: { + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: 10, + size: 0, + total: 0, + }, + syncJobsStatus: Status.IDLE, +}; + +describe('SyncJobsViewLogic', () => { + const { mount: indexViewLogicMount } = new LogicMounter(IndexViewLogic); + const { mount: fetchSyncJobsMount } = new LogicMounter(FetchSyncJobsApiLogic); + const { mount } = new LogicMounter(SyncJobsViewLogic); + + beforeEach(() => { + indexViewLogicMount(); + fetchSyncJobsMount(); + mount(); + }); + + it('has expected default values', () => { + expect(SyncJobsViewLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('actions', () => { + describe('FetchIndexApiLogic.apiSuccess', () => { + const syncJob = { + completed_at: '2022-09-05T15:59:39.816+00:00', + connector_id: 'we2284IBjobuR2-lAuXh', + created_at: '2022-09-05T14:59:39.816+00:00', + deleted_document_count: 20, + error: null, + index_name: 'indexName', + indexed_document_count: 50, + status: SyncStatus.COMPLETED, + worker_hostname: 'hostname_fake', + }; + const syncJobView: SyncJobView = { + docsCount: 30, + duration: moment.duration(1, 'hour'), + lastSync: syncJob.completed_at, + status: SyncStatus.COMPLETED, + }; + it('should update values', async () => { + FetchSyncJobsApiLogic.actions.apiSuccess({ + data: [syncJob], + has_more_hits_than_total: false, + pageIndex: 3, + pageSize: 20, + size: 20, + total: 50, + }); + await nextTick(); + expect(SyncJobsViewLogic.values).toEqual({ + ...DEFAULT_VALUES, + syncJobs: [syncJobView], + syncJobsData: { + data: [syncJob], + has_more_hits_than_total: false, + pageIndex: 3, + pageSize: 20, + size: 20, + total: 50, + }, + syncJobsLoading: false, + syncJobsPagination: { + has_more_hits_than_total: false, + pageIndex: 3, + pageSize: 20, + size: 20, + total: 50, + }, + syncJobsStatus: Status.SUCCESS, + }); + }); + it('should update values for incomplete job', async () => { + FetchSyncJobsApiLogic.actions.apiSuccess({ + data: [ + { + ...syncJob, + completed_at: null, + deleted_document_count: 0, + status: SyncStatus.IN_PROGRESS, + }, + ], + has_more_hits_than_total: false, + pageIndex: 3, + pageSize: 20, + size: 20, + total: 50, + }); + await nextTick(); + expect(SyncJobsViewLogic.values).toEqual({ + ...DEFAULT_VALUES, + syncJobs: [ + { + docsCount: 50, + duration: undefined, + lastSync: syncJob.created_at, + status: SyncStatus.IN_PROGRESS, + }, + ], + syncJobsData: { + data: [ + { + ...syncJob, + completed_at: null, + deleted_document_count: 0, + status: SyncStatus.IN_PROGRESS, + }, + ], + has_more_hits_than_total: false, + pageIndex: 3, + pageSize: 20, + size: 20, + total: 50, + }, + syncJobsLoading: false, + syncJobsPagination: { + has_more_hits_than_total: false, + pageIndex: 3, + pageSize: 20, + size: 20, + total: 50, + }, + syncJobsStatus: Status.SUCCESS, + }); + }); + }); + }); + + describe('listeners', () => { + it('calls clearFlashMessages on fetchSyncJobs', async () => { + SyncJobsViewLogic.actions.fetchSyncJobs({ connectorId: 'connectorId' }); + await nextTick(); + expect(mockFlashMessageHelpers.clearFlashMessages).toHaveBeenCalledTimes(1); + }); + + it('calls flashAPIErrors on apiError', async () => { + SyncJobsViewLogic.actions.fetchSyncJobsError({} as HttpError); + await nextTick(); + expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledTimes(1); + expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledWith({}); + expect(SyncJobsViewLogic.values).toEqual({ + ...DEFAULT_VALUES, + syncJobsLoading: false, + syncJobsStatus: Status.ERROR, + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs_view_logic.ts new file mode 100644 index 0000000000000..0b2dff7d6d5eb --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs_view_logic.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import moment from 'moment'; + +import { Status } from '../../../../../common/types/api'; + +import { ConnectorSyncJob, SyncStatus } from '../../../../../common/types/connectors'; +import { Paginate } from '../../../../../common/types/pagination'; +import { Actions } from '../../../shared/api_logic/create_api_logic'; +import { clearFlashMessages, flashAPIErrors } from '../../../shared/flash_messages'; +import { + FetchSyncJobsApiLogic, + FetchSyncJobsArgs, + FetchSyncJobsResponse, +} from '../../api/connector/fetch_sync_jobs_api_logic'; + +import { IndexViewLogic } from './index_view_logic'; + +export interface SyncJobView { + docsCount: number; + duration: moment.Duration; + lastSync: string; + status: SyncStatus; +} + +export interface IndexViewActions { + fetchSyncJobs: Actions['makeRequest']; + fetchSyncJobsError: Actions['apiError']; +} + +export interface IndexViewValues { + connectorId: string | null; + syncJobs: SyncJobView[]; + syncJobsData: Paginate | null; + syncJobsLoading: boolean; + syncJobsPagination: Paginate; + syncJobsStatus: Status; +} + +export const SyncJobsViewLogic = kea>({ + actions: {}, + connect: { + actions: [ + FetchSyncJobsApiLogic, + [ + 'apiError as fetchSyncJobsError', + 'apiReset as resetFetchSyncJobsIndexApi', + 'apiSuccess as fetchSyncJobsApiSuccess', + 'makeRequest as fetchSyncJobs', + ], + ], + values: [ + IndexViewLogic, + ['connectorId'], + FetchSyncJobsApiLogic, + ['data as syncJobsData', 'status as syncJobsStatus'], + ], + }, + listeners: () => ({ + fetchSyncJobs: () => clearFlashMessages(), + fetchSyncJobsError: (e) => flashAPIErrors(e), + }), + path: ['enterprise_search', 'content', 'sync_jobs_view_logic'], + selectors: ({ selectors }) => ({ + syncJobs: [ + () => [selectors.syncJobsData], + (data?: Paginate) => + data?.data.map((syncJob) => { + return { + docsCount: syncJob.deleted_document_count + ? syncJob.indexed_document_count - syncJob.deleted_document_count + : syncJob.indexed_document_count, + duration: syncJob.completed_at + ? moment.duration(moment(syncJob.completed_at).diff(moment(syncJob.created_at))) + : undefined, + lastSync: syncJob.completed_at ?? syncJob.created_at, + status: syncJob.status, + }; + }) ?? [], + ], + syncJobsLoading: [ + () => [selectors.syncJobsStatus], + (status: Status) => status === Status.IDLE || status === Status.LOADING, + ], + syncJobsPagination: [ + () => [selectors.syncJobsData], + (data?: Paginate) => + data + ? { ...data, data: undefined } + : { + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: 10, + size: 0, + total: 0, + }, + ], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/duration_to_text.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/duration_to_text.test.ts new file mode 100644 index 0000000000000..7615332b68959 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/duration_to_text.test.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 moment from 'moment'; + +import { durationToText } from './duration_to_text'; + +describe('durationToText', () => { + it('should correctly turn duration into text', () => { + expect(durationToText(moment.duration(11005, 'seconds'))).toEqual('3h 3m 25s'); + }); + it('should return -- for undefined', () => { + expect(durationToText(undefined)).toEqual('--'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/duration_to_text.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/duration_to_text.ts new file mode 100644 index 0000000000000..e4d678666c75e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/duration_to_text.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 moment from 'moment'; + +export function durationToText(input?: moment.Duration): string { + if (input) { + const hours = input.hours(); + const minutes = input.minutes(); + const seconds = input.seconds(); + return `${hours}h ${minutes}m ${seconds}s`; + } else { + return '--'; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/sync_status_to_text.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/sync_status_to_text.test.ts new file mode 100644 index 0000000000000..d9ecdf6ebc556 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/sync_status_to_text.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SyncStatus } from '../../../../common/types/connectors'; + +import { syncStatusToColor, syncStatusToText } from './sync_status_to_text'; + +describe('syncStatusToText', () => { + it('should return correct value for completed', () => { + expect(syncStatusToText(SyncStatus.COMPLETED)).toEqual('Sync complete'); + }); + it('should return correct value for error', () => { + expect(syncStatusToText(SyncStatus.ERROR)).toEqual('Sync failure'); + }); + it('should return correct value for in progress', () => { + expect(syncStatusToText(SyncStatus.IN_PROGRESS)).toEqual('Sync in progress'); + }); +}); + +describe('syncStatusToColor', () => { + it('should return correct value for completed', () => { + expect(syncStatusToColor(SyncStatus.COMPLETED)).toEqual('success'); + }); + it('should return correct value for error', () => { + expect(syncStatusToColor(SyncStatus.ERROR)).toEqual('danger'); + }); + it('should return correct value for in progress', () => { + expect(syncStatusToColor(SyncStatus.IN_PROGRESS)).toEqual('warning'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/sync_status_to_text.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/sync_status_to_text.ts new file mode 100644 index 0000000000000..51d69f2e53ad7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/sync_status_to_text.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 { i18n } from '@kbn/i18n'; + +import { SyncStatus } from '../../../../common/types/connectors'; + +export function syncStatusToText(status: SyncStatus): string { + switch (status) { + case SyncStatus.COMPLETED: + return i18n.translate('xpack.enterpriseSearch.content.syncStatus.completed', { + defaultMessage: 'Sync complete', + }); + case SyncStatus.ERROR: + return i18n.translate('xpack.enterpriseSearch.content.syncStatus.error', { + defaultMessage: 'Sync failure', + }); + case SyncStatus.IN_PROGRESS: + return i18n.translate('xpack.enterpriseSearch.content.syncStatus.inProgress', { + defaultMessage: 'Sync in progress', + }); + } +} + +export function syncStatusToColor(status: SyncStatus): string { + switch (status) { + case SyncStatus.COMPLETED: + return 'success'; + case SyncStatus.ERROR: + return 'danger'; + case SyncStatus.IN_PROGRESS: + return 'warning'; + } +} diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.test.ts new file mode 100644 index 0000000000000..d06d9af1dbaf8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.test.ts @@ -0,0 +1,143 @@ +/* + * Copyright 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 { setupConnectorsIndices } from '../../index_management/setup_indices'; + +import { fetchSyncJobsByConnectorId } from './fetch_sync_jobs'; + +jest.mock('../../index_management/setup_indices', () => ({ + setupConnectorsIndices: jest.fn(), +})); + +describe('fetchSyncJobs lib', () => { + const mockClient = { + asCurrentUser: { + get: jest.fn(), + search: jest.fn(), + }, + asInternalUser: {}, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('fetch sync jobs by connector id', () => { + it('should fetch sync jobs by connector id', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.resolve({ hits: { hits: ['result1', 'result2'] }, total: 2 }) + ); + await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 10)).resolves.toEqual({ + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: 10, + size: 0, + total: 0, + }); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: '.elastic-connectors-sync-jobs', + query: { + term: { + connector_id: 'id', + }, + }, + size: 10, + sort: { + created_at: { + order: 'desc', + }, + }, + }); + }); + it('should return empty result if size is 0', async () => { + await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 0)).resolves.toEqual({ + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: 0, + size: 0, + total: 0, + }); + expect(mockClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + it('should call setup connectors on index not found error', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { + type: 'index_not_found_exception', + }, + }, + }, + }) + ); + await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 10)).resolves.toEqual({ + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: 10, + size: 0, + total: 0, + }); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: '.elastic-connectors-sync-jobs', + query: { + term: { + connector_id: 'id', + }, + }, + size: 10, + sort: { + created_at: { + order: 'desc', + }, + }, + }); + expect(setupConnectorsIndices as jest.Mock).toHaveBeenCalledWith(mockClient.asCurrentUser); + }); + it('should not call setup connectors on other errors', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { + type: 'other error', + }, + }, + }, + }) + ); + await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 10)).resolves.toEqual({ + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: 10, + size: 0, + total: 0, + }); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: '.elastic-connectors-sync-jobs', + query: { + term: { + connector_id: 'id', + }, + }, + size: 10, + sort: { + created_at: { + order: 'desc', + }, + }, + }); + expect(setupConnectorsIndices as jest.Mock).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.ts new file mode 100644 index 0000000000000..451b8e7ad3e6e --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.ts @@ -0,0 +1,85 @@ +/* + * Copyright 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 { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; +import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; + +import { CONNECTORS_JOBS_INDEX } from '../..'; +import { ConnectorSyncJob } from '../../../common/types/connectors'; +import { Paginate } from '../../../common/types/pagination'; +import { isNotNullish } from '../../../common/utils/is_not_nullish'; + +import { setupConnectorsIndices } from '../../index_management/setup_indices'; +import { isIndexNotFoundException } from '../../utils/identify_exceptions'; + +export const fetchSyncJobsByConnectorId = async ( + client: IScopedClusterClient, + connectorId: string, + pageIndex: number, + size: number +): Promise> => { + try { + if (size === 0) { + // prevent some divide by zero errors below + return { + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: size, + size: 0, + total: 0, + }; + } + const result = await client.asCurrentUser.search({ + from: pageIndex * size, + index: CONNECTORS_JOBS_INDEX, + query: { + term: { + connector_id: connectorId, + }, + }, + size, + // @ts-ignore Elasticsearch-js has the wrong internal typing for this field + sort: { created_at: { order: 'desc' } }, + }); + const total = totalToPaginateTotal(result.hits.total); + // If we get fewer results than the target page, make sure we return correct page we're on + const resultPageIndex = Math.min(pageIndex, Math.trunc(total.total / size)); + const data = result.hits.hits.map((hit) => hit._source).filter(isNotNullish) ?? []; + return { + data, + pageIndex: resultPageIndex, + pageSize: size, + size: data.length, + ...total, + }; + } catch (error) { + if (isIndexNotFoundException(error)) { + await setupConnectorsIndices(client.asCurrentUser); + } + return { + data: [], + has_more_hits_than_total: false, + pageIndex: 0, + pageSize: size, + size: 0, + total: 0, + }; + } +}; + +function totalToPaginateTotal(input: number | SearchTotalHits | undefined): { + has_more_hits_than_total: boolean; + total: number; +} { + if (typeof input === 'number') { + return { has_more_hits_than_total: false, total: input }; + } + return input + ? { has_more_hits_than_total: input.relation === 'gte' ? true : false, total: input.value } + : { has_more_hits_than_total: false, total: 0 }; +} diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts index e9ac4e96ba923..b17cbffd68c40 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { ErrorCode } from '../../../common/types/error_codes'; import { addConnector } from '../../lib/connectors/add_connector'; +import { fetchSyncJobsByConnectorId } from '../../lib/connectors/fetch_sync_jobs'; import { startConnectorSync } from '../../lib/connectors/start_sync'; import { updateConnectorConfiguration } from '../../lib/connectors/update_connector_configuration'; import { updateConnectorScheduling } from '../../lib/connectors/update_connector_scheduling'; @@ -111,4 +112,29 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) { return response.ok(); }) ); + + router.get( + { + path: '/internal/enterprise_search/connectors/{connectorId}/sync_jobs', + validate: { + params: schema.object({ + connectorId: schema.string(), + }), + query: schema.object({ + page: schema.number({ defaultValue: 0, min: 0 }), + size: schema.number({ defaultValue: 10, min: 0 }), + }), + }, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + const result = await fetchSyncJobsByConnectorId( + client, + request.params.connectorId, + request.query.page, + request.query.size + ); + return response.ok({ body: result }); + }) + ); } diff --git a/x-pack/plugins/files/public/files_client/files_client.ts b/x-pack/plugins/files/public/files_client/files_client.ts index 2f01405ee1492..922846cde2aaf 100644 --- a/x-pack/plugins/files/public/files_client/files_client.ts +++ b/x-pack/plugins/files/public/files_client/files_client.ts @@ -8,7 +8,7 @@ import { pipe } from 'fp-ts/lib/function'; import * as qs from 'query-string'; import type { HttpStart } from '@kbn/core/public'; -import type { FilesClient } from '../types'; +import type { ScopedFilesClient, FilesClient } from '../types'; import { API_BASE_PATH, FILES_API_BASE_PATH, @@ -65,47 +65,70 @@ export const apiRoutes = { getMetricsRoute: () => `${API_BASE_PATH}/metrics`, }; -interface Args { - fileKind: string; +/** + * Arguments to create a new {@link FileClient}. + */ +export interface Args { + /** + * The http start service from core. + */ http: HttpStart; } +/** + * Arguments to create a new {@link ScopedFilesClient}. + */ +export interface ScopedArgs extends Args { + /** + * The file kind to scope all requests to where file kinds are needed. + */ + fileKind: string; +} + const commonBodyHeaders = { headers: { 'content-type': 'application/json', }, }; -export const createFilesClient = ({ http, fileKind }: Args): FilesClient => { +export function createFilesClient(args: Args): FilesClient; +export function createFilesClient(scopedArgs: ScopedArgs): ScopedFilesClient; +export function createFilesClient({ + http, + fileKind: scopedFileKind, +}: { + http: HttpStart; + fileKind?: string; +}): FilesClient | ScopedFilesClient { const api: FilesClient = { - create: (args) => { - return http.post(apiRoutes.getCreateFileRoute(fileKind), { + create: ({ kind, ...args }) => { + return http.post(apiRoutes.getCreateFileRoute(scopedFileKind ?? kind), { headers: commonBodyHeaders, body: JSON.stringify(args), }); }, - delete: (args) => { - return http.delete(apiRoutes.getDeleteRoute(fileKind, args.id)); + delete: ({ kind, ...args }) => { + return http.delete(apiRoutes.getDeleteRoute(scopedFileKind ?? kind, args.id)); }, - download: (args) => { - return http.get(apiRoutes.getDownloadRoute(fileKind, args.id, args.fileName), { + download: ({ kind, ...args }) => { + return http.get(apiRoutes.getDownloadRoute(scopedFileKind ?? kind, args.id, args.fileName), { headers: { Accept: '*/*' }, }); }, - getById: (args) => { - return http.get(apiRoutes.getByIdRoute(fileKind, args.id)); + getById: ({ kind, ...args }) => { + return http.get(apiRoutes.getByIdRoute(scopedFileKind ?? kind, args.id)); }, - list(args = {}) { - return http.get(apiRoutes.getListRoute(fileKind, args.page, args.perPage)); + list({ kind, ...args } = { kind: '' }) { + return http.get(apiRoutes.getListRoute(scopedFileKind ?? kind, args.page, args.perPage)); }, - update: ({ id, ...body }) => { - return http.patch(apiRoutes.getUpdateRoute(fileKind, id), { + update: ({ kind, id, ...body }) => { + return http.patch(apiRoutes.getUpdateRoute(scopedFileKind ?? kind, id), { headers: commonBodyHeaders, body: JSON.stringify(body), }); }, - upload: (args) => { - return http.put(apiRoutes.getUploadRoute(fileKind, args.id), { + upload: ({ kind, ...args }) => { + return http.put(apiRoutes.getUploadRoute(scopedFileKind ?? kind, args.id), { headers: { 'Content-Type': 'application/octet-stream', }, @@ -113,20 +136,25 @@ export const createFilesClient = ({ http, fileKind }: Args): FilesClient => { body: args.body as BodyInit, }); }, - share: ({ fileId, name, validUntil }) => { - return http.post(apiRoutes.getShareRoute(fileKind, fileId), { + getDownloadHref: ({ fileKind: kind, id }) => { + return `${http.basePath.prepend(apiRoutes.getDownloadRoute(scopedFileKind ?? kind, id))}`; + }, + share: ({ kind, fileId, name, validUntil }) => { + return http.post(apiRoutes.getShareRoute(scopedFileKind ?? kind, fileId), { headers: commonBodyHeaders, body: JSON.stringify({ name, validUntil }), }); }, - unshare: ({ id }) => { - return http.delete(apiRoutes.getShareRoute(fileKind, id)); + unshare: ({ kind, id }) => { + return http.delete(apiRoutes.getShareRoute(scopedFileKind ?? kind, id)); }, - getShare: ({ id }) => { - return http.get(apiRoutes.getShareRoute(fileKind, id)); + getShare: ({ kind, id }) => { + return http.get(apiRoutes.getShareRoute(scopedFileKind ?? kind, id)); }, - listShares: ({ forFileId, page, perPage }) => { - return http.get(apiRoutes.getListSharesRoute(fileKind, page, perPage, forFileId)); + listShares: ({ kind, forFileId, page, perPage }) => { + return http.get( + apiRoutes.getListSharesRoute(scopedFileKind ?? kind, page, perPage, forFileId) + ); }, find: ({ page, perPage, ...filterArgs }) => { return http.post(apiRoutes.getFindRoute(page, perPage), { @@ -140,9 +168,6 @@ export const createFilesClient = ({ http, fileKind }: Args): FilesClient => { publicDownload: ({ token, fileName }) => { return http.get(apiRoutes.getPublicDownloadRoute(token, fileName)); }, - getDownloadHref: ({ id }) => { - return `${http.basePath.prepend(apiRoutes.getDownloadRoute(fileKind, id))}`; - }, }; return api; -}; +} diff --git a/x-pack/plugins/files/public/index.ts b/x-pack/plugins/files/public/index.ts index 36a570c209683..0c8d9dd242b51 100644 --- a/x-pack/plugins/files/public/index.ts +++ b/x-pack/plugins/files/public/index.ts @@ -7,7 +7,12 @@ import { FilesPlugin } from './plugin'; export type { FilesSetup, FilesStart } from './plugin'; -export type { FilesClient, FilesClientFactory, FilesClientResponses } from './types'; +export type { + FilesClient, + ScopedFilesClient, + FilesClientFactory, + FilesClientResponses, +} from './types'; export function plugin() { return new FilesPlugin(); diff --git a/x-pack/plugins/files/public/plugin.ts b/x-pack/plugins/files/public/plugin.ts index 92358d067d36a..22276ba377d8e 100644 --- a/x-pack/plugins/files/public/plugin.ts +++ b/x-pack/plugins/files/public/plugin.ts @@ -34,6 +34,9 @@ export class FilesPlugin implements Plugin { asScoped(fileKind: string) { return createFilesClient({ fileKind, http: core.http }); }, + asUnscoped() { + return createFilesClient({ http: core.http }); + }, }, }; return this.api; diff --git a/x-pack/plugins/files/public/types.ts b/x-pack/plugins/files/public/types.ts index 3a52b863bc7e4..8a2a67bea8e93 100644 --- a/x-pack/plugins/files/public/types.ts +++ b/x-pack/plugins/files/public/types.ts @@ -24,21 +24,43 @@ import type { HttpApiInterfaceEntryDefinition, } from '../common/api_routes'; +type UnscopedClientMethodFrom = ( + args: E['inputs']['body'] & E['inputs']['params'] & E['inputs']['query'] +) => Promise; + /** * @param args - Input to the endpoint which includes body, params and query of the RESTful endpoint. */ type ClientMethodFrom = ( - args: E['inputs']['body'] & E['inputs']['params'] & E['inputs']['query'] + args: Parameters>[0] & { kind: string } ) => Promise; -type ClientMethodOptionalArgsFrom = ( - args?: E['inputs']['body'] & E['inputs']['params'] & E['inputs']['query'] -) => Promise; +interface GlobalEndpoints { + /** + * Get metrics of file system, like storage usage. + * + * @param args - Get metrics arguments + */ + getMetrics: () => Promise; + /** + * Download a file, bypassing regular security by way of a + * secret share token. + * + * @param args - Get public download arguments. + */ + publicDownload: UnscopedClientMethodFrom; + /** + * Find a set of files given some filters. + * + * @param args - File filters + */ + find: UnscopedClientMethodFrom; +} /** * A client that can be used to manage a specific {@link FileKind}. */ -export interface FilesClient { +export interface FilesClient extends GlobalEndpoints { /** * Create a new file object with the provided metadata. * @@ -62,13 +84,7 @@ export interface FilesClient { * * @param args - list files args */ - list: ClientMethodOptionalArgsFrom; - /** - * Find a set of files given some filters. - * - * @param args - File filters - */ - find: ClientMethodFrom; + list: ClientMethodFrom; /** * Update a set of of metadata values of the file object. * @@ -87,6 +103,11 @@ export interface FilesClient { * @param args - download file args */ download: ClientMethodFrom; + /** + * Get a string for downloading a file that can be passed to a button element's + * href for download. + */ + getDownloadHref: (file: FileJSON) => string; /** * Share a file by creating a new file share instance. * @@ -115,25 +136,6 @@ export interface FilesClient { * @param args - Get file share arguments */ listShares: ClientMethodFrom; - /** - * Get metrics of file system, like storage usage. - * - * @param args - Get metrics arguments - */ - getMetrics: ClientMethodFrom; - /** - * Download a file, bypassing regular security by way of a - * secret share token. - * - * @param args - Get public download arguments. - */ - publicDownload: ClientMethodFrom; - - /** - * Get a string for downloading a file that can be passed to a button element's - * href for download. - */ - getDownloadHref: (file: FileJSON) => string; } export type FilesClientResponses = { @@ -141,13 +143,29 @@ export type FilesClientResponses = { }; /** - * A factory for creating a {@link FilesClient} + * A files client that is scoped to a specific {@link FileKind}. + * + * More convenient if you want to re-use the same client for the same file kind + * and not specify the kind every time. + */ +export type ScopedFilesClient = { + [K in keyof FilesClient]: K extends 'list' + ? (arg?: Omit[0], 'kind'>) => ReturnType + : (arg: Omit[0], 'kind'>) => ReturnType; +}; + +/** + * A factory for creating a {@link ScopedFilesClient} */ export interface FilesClientFactory { /** - * Create a {@link FileClient} for a given {@link FileKind}. + * Create a files client. + */ + asUnscoped(): FilesClient; + /** + * Create a {@link ScopedFileClient} for a given {@link FileKind}. * * @param fileKind - The {@link FileKind} to create a client for. */ - asScoped(fileKind: string): FilesClient; + asScoped(fileKind: string): ScopedFilesClient; } diff --git a/x-pack/plugins/fleet/cypress/integration/a11y/home_page.spec.ts b/x-pack/plugins/fleet/cypress/integration/a11y/home_page.spec.ts index f923187c93653..b5d6e9d605f1e 100644 --- a/x-pack/plugins/fleet/cypress/integration/a11y/home_page.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/a11y/home_page.spec.ts @@ -9,50 +9,50 @@ import 'cypress-real-events/support'; import { checkA11y } from '../../support/commands'; import { FLEET, navigateTo } from '../../tasks/navigation'; import { + AGENT_FLYOUT, GENERATE_FLEET_SERVER_POLICY_BUTTON, - AGENTS_QUICK_START_TAB_BUTTON, PLATFORM_TYPE_LINUX_BUTTON, - AGENTS_ADVANCED_TAB_BUTTON, ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON, ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON, AGENT_POLICIES_TAB, - AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON, - AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT_TITLE, + AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT, AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD, AGENT_POLICIES_FLYOUT_ADVANCED_DEFAULT_NAMESPACE_HEADER, AGENT_POLICY_FLYOUT_CREATE_BUTTON, ENROLLMENT_TOKENS_TAB, - ENROLLMENT_TOKENS_CREATE_TOKEN_BUTTON, - ENROLLMENT_TOKENS_CREATE_TOKEN_NAME_FIELD, + ENROLLMENT_TOKENS, DATA_STREAMS_TAB, SETTINGS_TAB, SETTINGS_FLEET_SERVER_HOST_HEADING, + FLEET_SERVER_HOST_INPUT, } from '../../screens/fleet'; import { AGENT_POLICY_NAME_LINK } from '../../screens/integrations'; import { cleanupAgentPolicies, unenrollAgent } from '../../tasks/cleanup'; describe('Home page', () => { before(() => { navigateTo(FLEET); - cy.getBySel(AGENTS_QUICK_START_TAB_BUTTON, { timeout: 15000 }).should('be.visible'); + cy.getBySel(AGENT_FLYOUT.QUICK_START_TAB_BUTTON, { timeout: 15000 }).should('be.visible'); }); describe('Agents', () => { const fleetServerHost = 'https://localhost:8220'; + describe('Quick Start', () => { it('Get started with fleet', () => { checkA11y({ skipFailures: false }); }); it('Install Fleet Server', () => { - cy.getBySel('fleetServerHostInput', { timeout: 15000 }).should('be.visible'); - cy.getBySel('fleetServerHostInput').getBySel('comboBoxSearchInput').type(fleetServerHost); + cy.getBySel(FLEET_SERVER_HOST_INPUT, { timeout: 15000 }).should('be.visible'); + cy.getBySel(FLEET_SERVER_HOST_INPUT).getBySel('comboBoxSearchInput').type(fleetServerHost); cy.getBySel(GENERATE_FLEET_SERVER_POLICY_BUTTON).click(); cy.getBySel(PLATFORM_TYPE_LINUX_BUTTON, { timeout: 15000 }).should('be.visible'); checkA11y({ skipFailures: false }); }); }); + describe('Advanced', () => { before(() => { - cy.getBySel(AGENTS_ADVANCED_TAB_BUTTON).click(); + cy.getBySel(AGENT_FLYOUT.ADVANCED_TAB_BUTTON).click(); }); it('Select policy for fleet', () => { checkA11y({ skipFailures: false }); @@ -69,19 +69,20 @@ describe('Home page', () => { }); }); }); + describe('Agent Policies', () => { before(() => { cy.getBySel(AGENT_POLICIES_TAB).click(); - cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON, { timeout: 15000 }).should( - 'be.visible' - ); + cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT.CREATE_BUTTON, { + timeout: 15000, + }).should('be.visible'); }); it('Agent Table', () => { checkA11y({ skipFailures: false }); }); it('Create Policy Flyout', () => { - cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON).click(); - cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT_TITLE, { timeout: 15000 }).should( + cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT.CREATE_BUTTON).click(); + cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT.TITLE, { timeout: 15000 }).should( 'be.visible' ); cy.getBySel(AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD).type('testName'); @@ -97,6 +98,7 @@ describe('Home page', () => { checkA11y({ skipFailures: true }); }); }); + describe('Enrollment Tokens', () => { before(() => { cy.getBySel(ENROLLMENT_TOKENS_TAB).click(); @@ -106,13 +108,14 @@ describe('Home page', () => { checkA11y({ skipFailures: false }); }); it('Create Enrollment Token Modal', () => { - cy.getBySel(ENROLLMENT_TOKENS_CREATE_TOKEN_BUTTON).click(); - cy.getBySel(ENROLLMENT_TOKENS_CREATE_TOKEN_NAME_FIELD, { timeout: 15000 }).should( + cy.getBySel(ENROLLMENT_TOKENS.CREATE_TOKEN_BUTTON).click(); + cy.getBySel(ENROLLMENT_TOKENS.CREATE_TOKEN_MODAL_NAME_FIELD, { timeout: 15000 }).should( 'be.visible' ); checkA11y({ skipFailures: false }); }); }); + describe('Data Streams', () => { before(() => { cy.getBySel('confirmModalCancelButton').click(); diff --git a/x-pack/plugins/fleet/cypress/integration/agent_binary_download_source.spec.ts b/x-pack/plugins/fleet/cypress/integration/agent_binary_download_source.spec.ts index c695939248826..095a87a28e130 100644 --- a/x-pack/plugins/fleet/cypress/integration/agent_binary_download_source.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/agent_binary_download_source.spec.ts @@ -10,10 +10,10 @@ import { AGENT_BINARY_SOURCES_TABLE_ACTIONS, AGENT_BINARY_SOURCES_FLYOUT, AGENT_POLICY_FORM, - CONFIRM_MODAL_CONFIRM_BUTTON, } from '../screens/fleet'; import { cleanupDownloadSources } from '../tasks/cleanup'; import { FLEET, navigateTo } from '../tasks/navigation'; +import { CONFIRM_MODAL } from '../screens/navigation'; describe('Agent binary download source section', () => { beforeEach(() => { @@ -35,7 +35,7 @@ describe('Agent binary download source section', () => { .clear() .type('https://edited-default-host.co'); cy.getBySel(AGENT_BINARY_SOURCES_FLYOUT.SUBMIT_BUTTON).click(); - cy.getBySel(CONFIRM_MODAL_CONFIRM_BUTTON).click(); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); cy.intercept('api/fleet/agent_download_sources/fleet-default-download-source', { host: 'https://edited-default-host.co', diff --git a/x-pack/plugins/fleet/cypress/integration/agent.spec.ts b/x-pack/plugins/fleet/cypress/integration/agent_list.spec.ts similarity index 54% rename from x-pack/plugins/fleet/cypress/integration/agent.spec.ts rename to x-pack/plugins/fleet/cypress/integration/agent_list.spec.ts index 084ba548ff6c6..bc3cf5c2f6ed3 100644 --- a/x-pack/plugins/fleet/cypress/integration/agent.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/agent_list.spec.ts @@ -5,7 +5,11 @@ * 2.0. */ +import { FLEET_AGENT_LIST_PAGE } from '../screens/fleet'; + import { createAgentDoc } from '../tasks/agents'; +import { setupFleetServer } from '../tasks/fleet_server'; +import { deleteFleetServerDocs, deleteAgentDocs } from '../tasks/cleanup'; const createAgentDocs = (kibanaVersion: string) => [ createAgentDoc('agent-1', 'policy-1'), // this agent will have upgrade available @@ -14,29 +18,39 @@ const createAgentDocs = (kibanaVersion: string) => [ ]; let docs: any[] = []; -// TODO: create fleet server, fix version of agent to upgrade to an allowed version (>= fleet server's, < kibana) -// https://github.com/elastic/kibana/issues/138121 -describe.skip('View agents', () => { + +describe('View agents list', () => { before(() => { - cy.task('deleteDocsByQuery', { - index: '.fleet-agents', - query: { match_all: {} }, - ignoreUnavailable: true, - }); + deleteFleetServerDocs(true); + deleteAgentDocs(true); + setupFleetServer(); + cy.getKibanaVersion().then((version) => { docs = createAgentDocs(version); cy.task('insertDocs', { index: '.fleet-agents', docs }); }); }); after(() => { - cy.task('deleteDocsByQuery', { - index: '.fleet-agents', - query: { match_all: {} }, - }); + deleteFleetServerDocs(); + deleteAgentDocs(); }); beforeEach(() => { - cy.intercept('/api/fleet/agents/setup', { isReady: true }); + cy.intercept('/api/fleet/agents/setup', { + isReady: true, + missing_optional_features: [], + missing_requirements: [], + }); cy.intercept('/api/fleet/setup', { isInitialized: true, nonFatalErrors: [] }); + cy.intercept('/api/fleet/agents_status', { + total: 18, + inactive: 0, + online: 18, + error: 0, + offline: 0, + updating: 0, + other: 0, + events: 0, + }); cy.intercept(/\/api\/fleet\/agent_policies(\?.*)?$/, { items: [ { @@ -78,10 +92,10 @@ describe.skip('View agents', () => { describe('Agent filter suggestions', () => { it('should filter based on agent id', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.queryInput').type('agent.id: "agent-1"{enter}'); - cy.getBySel('fleetAgentListTable'); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 2); - cy.getBySel('fleetAgentListTable').contains('agent-1'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.QUERY_INPUT).type('agent.id: "agent-1"{enter}'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 2); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-1'); }); }); @@ -89,19 +103,19 @@ describe.skip('View agents', () => { it('should only show agents with upgrade available after click', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.showUpgradeable').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 17); - cy.getBySel('fleetAgentListTable').contains('agent-1'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.SHOW_UPGRADEABLE).click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 17); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-1'); }); it('should clear filter on second click', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.showUpgradeable').click(); - cy.getBySel('agentList.showUpgradeable').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 18); - cy.getBySel('fleetAgentListTable').contains('agent-1'); - cy.getBySel('fleetAgentListTable').contains('agent-2'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.SHOW_UPGRADEABLE).click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.SHOW_UPGRADEABLE).click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 19); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-1'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-2'); }); }); @@ -109,85 +123,92 @@ describe.skip('View agents', () => { it('should should show all policies as options', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.policyFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.POLICY_FILTER).click(); cy.get('button').contains('Agent policy 1'); cy.get('button').contains('Agent policy 2'); cy.get('button').contains('Agent policy 3'); }); + it('should filter on single policy (no results)', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.policyFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.POLICY_FILTER).click(); cy.get('button').contains('Agent policy 4').click(); - cy.getBySel('fleetAgentListTable').contains('No agents found'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('No agents found'); }); + it('should filter on single policy', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.policyFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.POLICY_FILTER).click(); cy.get('button').contains('Agent policy 1').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 2); - cy.getBySel('fleetAgentListTable').contains('agent-1'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 2); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-1'); }); + it('should filter on multiple policies', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.policyFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.POLICY_FILTER).click(); cy.get('button').contains('Agent policy 1').click(); cy.get('button').contains('Agent policy 2').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 3); - cy.getBySel('fleetAgentListTable').contains('agent-1'); - cy.getBySel('fleetAgentListTable').contains('agent-2'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 3); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-1'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-2'); }); }); + describe('Agent status filter', () => { it('should filter on healthy (16 result)', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.statusFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.STATUS_FILTER).click(); cy.get('button').contains('Healthy').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 17); - cy.getBySel('fleetAgentListTable').contains('agent-1'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 18); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-1'); }); + it('should filter on unhealthy (1 result)', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.statusFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.STATUS_FILTER).click(); cy.get('button').contains('Unhealthy').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 2); - cy.getBySel('fleetAgentListTable').contains('agent-2'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 2); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-2'); }); + it('should filter on inactive (0 result)', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.statusFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.STATUS_FILTER).click(); cy.get('button').contains('Inactive').click(); - cy.getBySel('fleetAgentListTable').contains('No agents found'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('No agents found'); }); + it('should filter on healthy and unhealthy', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.statusFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.STATUS_FILTER).click(); cy.get('button').contains('healthy').click(); cy.get('button').contains('Unhealthy').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 18); - cy.getBySel('fleetAgentListTable').contains('agent-1'); - cy.getBySel('fleetAgentListTable').contains('agent-2'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 19); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-1'); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).contains('agent-2'); }); }); @@ -195,19 +216,19 @@ describe.skip('View agents', () => { it('should allow to bulk upgrade agents', () => { cy.visit('/app/fleet/agents'); - cy.getBySel('agentList.policyFilter').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.POLICY_FILTER).click(); cy.get('button').contains('Agent policy 3').click(); - cy.getBySel('fleetAgentListTable').find('tr').should('have.length', 16); + cy.getBySel(FLEET_AGENT_LIST_PAGE.TABLE).find('tr').should('have.length', 16); - cy.getBySel('checkboxSelectAll').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.CHECKBOX_SELECT_ALL).click(); // Trigger a bulk upgrade - cy.getBySel('agentBulkActionsButton').click(); + cy.getBySel(FLEET_AGENT_LIST_PAGE.BULK_ACTIONS_BUTTON).click(); cy.get('button').contains('Upgrade 15 agents').click(); cy.get('.euiModalFooter button').contains('Upgrade 15 agents').click(); - // Cancel upgrade - cy.getBySel('abortUpgradeBtn').click(); - cy.get('button').contains('Confirm').click(); + // Cancel upgrade - this assertion is currently flaky + // cy.getBySel(CURRENT_BULK_UPGRADES_CALLOUT.ABORT_BTN).click(); + // cy.get('button').contains('Confirm').click(); }); }); }); diff --git a/x-pack/plugins/fleet/cypress/integration/agent_policy.spec.ts b/x-pack/plugins/fleet/cypress/integration/agent_policy.spec.ts index 694cf6c1527ae..63da2cbbfa389 100644 --- a/x-pack/plugins/fleet/cypress/integration/agent_policy.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/agent_policy.spec.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { TOAST_CLOSE_BTN } from '../screens/navigation'; describe('Edit agent policy', () => { beforeEach(() => { @@ -33,7 +34,7 @@ describe('Edit agent policy', () => { it('should edit agent policy', () => { cy.visit('/app/fleet/policies/policy-1/settings'); - cy.getBySel('toastCloseButton').click(); + cy.getBySel(TOAST_CLOSE_BTN).click(); cy.get('[placeholder="Optional description"').clear().type('desc'); cy.intercept('/api/fleet/agent_policies/policy-1', { diff --git a/x-pack/plugins/fleet/cypress/integration/enrollment_token.spec.ts b/x-pack/plugins/fleet/cypress/integration/enrollment_token.spec.ts new file mode 100644 index 0000000000000..e60842a299267 --- /dev/null +++ b/x-pack/plugins/fleet/cypress/integration/enrollment_token.spec.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 { FLEET, navigateTo } from '../tasks/navigation'; +import { cleanupAgentPolicies } from '../tasks/cleanup'; +import { ENROLLMENT_TOKENS_TAB, ENROLLMENT_TOKENS } from '../screens/fleet'; + +describe('Enrollment token page', () => { + before(() => { + navigateTo(FLEET); + cy.request({ + method: 'POST', + url: '/api/fleet/agent_policies', + body: { + name: 'Agent policy 1', + namespace: 'default', + description: '', + monitoring_enabled: ['logs', 'metrics'], + id: 'agent-policy-1', + }, + headers: { 'kbn-xsrf': 'cypress' }, + }); + cy.getBySel(ENROLLMENT_TOKENS_TAB).click(); + }); + + after(() => { + cleanupAgentPolicies(); + }); + + it('Create new Token', () => { + cy.getBySel(ENROLLMENT_TOKENS.CREATE_TOKEN_BUTTON).click(); + cy.getBySel(ENROLLMENT_TOKENS.CREATE_TOKEN_MODAL_NAME_FIELD).clear().type('New Token'); + cy.getBySel(ENROLLMENT_TOKENS.CREATE_TOKEN_MODAL_SELECT_FIELD).contains('Agent policy 1'); + cy.get('.euiButton').contains('Create enrollment token').click({ force: true }); + + cy.getBySel(ENROLLMENT_TOKENS.LIST_TABLE, { timeout: 15000 }).contains('Agent policy 1'); + }); + + it('Delete Token - inactivates the token', () => { + cy.visit('app/fleet/enrollment-tokens'); + cy.getBySel(ENROLLMENT_TOKENS.LIST_TABLE).find('tr').should('have.length', 2); + cy.getBySel(ENROLLMENT_TOKENS.TABLE_REVOKE_BTN).first().click(); + cy.get('.euiPanel').contains('Are you sure you want to revoke'); + cy.get('.euiButton').contains('Revoke enrollment token').click({ force: true }); + + cy.getBySel(ENROLLMENT_TOKENS.TABLE_REVOKE_BTN).first().should('not.exist'); + }); +}); diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_agent_flyout.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_agent_flyout.spec.ts index 626fa1427f8f1..b907fb8ef4c79 100644 --- a/x-pack/plugins/fleet/cypress/integration/fleet_agent_flyout.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/fleet_agent_flyout.spec.ts @@ -5,25 +5,17 @@ * 2.0. */ -import { ADD_AGENT_BUTTON, ADD_AGENT_FLYOUT } from '../screens/fleet'; -import { cleanupAgentPolicies } from '../tasks/cleanup'; +import { ADD_AGENT_BUTTON, AGENT_FLYOUT } from '../screens/fleet'; +import { cleanupAgentPolicies, deleteFleetServerDocs, deleteAgentDocs } from '../tasks/cleanup'; import { createAgentDoc } from '../tasks/agents'; -import { setFleetServerHost } from '../tasks/fleet'; +import { setFleetServerHost } from '../tasks/fleet_server'; import { FLEET, navigateTo } from '../tasks/navigation'; const FLEET_SERVER_POLICY_ID = 'fleet-server-policy'; function cleanUp() { - cy.task('deleteDocsByQuery', { - index: '.fleet-agents', - query: { match_all: {} }, - ignoreUnavailable: true, - }); - cy.task('deleteDocsByQuery', { - index: '.fleet-servers', - query: { match_all: {} }, - ignoreUnavailable: true, - }); + deleteFleetServerDocs(true); + deleteAgentDocs(true); cleanupAgentPolicies(); } let kibanaVersion: string; @@ -80,7 +72,7 @@ describe('Fleet add agent flyout', () => { cy.getBySel(ADD_AGENT_BUTTON).click(); cy.intercept('POST', '/api/fleet/agent_policies?sys_monitoring=true').as('createAgentPolicy'); - cy.getBySel('createPolicyBtn').click(); + cy.getBySel(AGENT_FLYOUT.CREATE_POLICY_BUTTON).click(); let agentPolicyId: string; const startTime = Date.now(); @@ -89,7 +81,7 @@ describe('Fleet add agent flyout', () => { agentPolicyId = xhr.response.body.item.id; }); // verify create button changed to dropdown - cy.getBySel('agentPolicyDropdown'); + cy.getBySel(AGENT_FLYOUT.POLICY_DROPDOWN); cy.wrap(null).then(() => { cy.task('insertDoc', { @@ -99,7 +91,7 @@ describe('Fleet add agent flyout', () => { }); }); - cy.getBySel(ADD_AGENT_FLYOUT.CONFIRM_AGENT_ENROLLMENT_BUTTON); + cy.getBySel(AGENT_FLYOUT.CONFIRM_AGENT_ENROLLMENT_BUTTON); cy.wrap(null).then(() => { cy.task('insertDoc', { @@ -113,7 +105,7 @@ describe('Fleet add agent flyout', () => { }); }); - cy.getBySel(ADD_AGENT_FLYOUT.INCOMING_DATA_CONFIRMED_CALL_OUT); + cy.getBySel(AGENT_FLYOUT.INCOMING_DATA_CONFIRMED_CALL_OUT); }); }); }); diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts index 338030e91f3da..a1c4eef06bdb5 100644 --- a/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { CONFIRM_MODAL_BTN } from '../screens/integrations'; +import { TOAST_CLOSE_BTN, CONFIRM_MODAL } from '../screens/navigation'; +import { SETTINGS_SAVE_BTN, SETTINGS_OUTPUTS } from '../screens/fleet'; describe('Edit settings', () => { beforeEach(() => { @@ -25,11 +26,11 @@ describe('Edit settings', () => { }); cy.visit('/app/fleet/settings'); - cy.getBySel('toastCloseButton').click(); + cy.getBySel(TOAST_CLOSE_BTN).click(); }); it('should update Fleet server hosts', () => { - cy.getBySel('editHostsBtn').click(); + cy.getBySel(SETTINGS_OUTPUTS.EDIT_HOSTS_BTN).click(); cy.get('[placeholder="Specify host URL"').type('https://localhost:8220'); cy.intercept('/api/fleet/settings', { @@ -39,8 +40,8 @@ describe('Edit settings', () => { fleet_server_hosts: ['https://localhost:8220'], }).as('updateSettings'); - cy.getBySel('saveApplySettingsBtn').click(); - cy.getBySel(CONFIRM_MODAL_BTN).click(); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); cy.wait('@updateSettings').then((interception) => { expect(interception.request.body.fleet_server_hosts[0]).to.equal('https://localhost:8220'); @@ -48,8 +49,8 @@ describe('Edit settings', () => { }); it('should update outputs', () => { - cy.getBySel('editOutputBtn').click(); - cy.get('[placeholder="Specify name"').clear().type('output-1'); + cy.getBySel(SETTINGS_OUTPUTS.EDIT_BTN).click(); + cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT).clear().type('output-1'); cy.get('[placeholder="Specify host URL"').clear().type('http://elasticsearch:9200'); cy.intercept('/api/fleet/outputs', { @@ -71,8 +72,8 @@ describe('Edit settings', () => { is_default_monitoring: true, }).as('updateOutputs'); - cy.getBySel('saveApplySettingsBtn').click(); - cy.getBySel(CONFIRM_MODAL_BTN).click(); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); cy.wait('@updateOutputs').then((interception) => { expect(interception.request.body.name).to.equal('output-1'); @@ -80,9 +81,9 @@ describe('Edit settings', () => { }); it('should allow to create a logstash output', () => { - cy.getBySel('addOutputBtn').click(); - cy.get('[placeholder="Specify name"]').clear().type('output-logstash-1'); - cy.get('[placeholder="Specify type"]').select('logstash'); + cy.getBySel(SETTINGS_OUTPUTS.ADD_BTN).click(); + cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT).clear().type('output-logstash-1'); + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('logstash'); cy.get('[placeholder="Specify host"').clear().type('logstash:5044'); cy.get('[placeholder="Specify ssl certificate"]').clear().type('SSL CERTIFICATE'); cy.get('[placeholder="Specify certificate key"]').clear().type('SSL KEY'); @@ -110,7 +111,7 @@ describe('Edit settings', () => { }, }).as('postLogstashOutput'); - cy.getBySel('saveApplySettingsBtn').click(); + cy.getBySel(SETTINGS_SAVE_BTN).click(); cy.wait('@postLogstashOutput').then((interception) => { expect(interception.request.body.name).to.equal('output-logstash-1'); diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts index 7d7f0d1c5119c..befa2074ac865 100644 --- a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts @@ -8,9 +8,12 @@ import { AGENTS_TAB, ADD_AGENT_BUTTON_TOP, - AGENT_FLYOUT_CLOSE_BUTTON, - STANDALONE_TAB, - AGENT_POLICY_CODE_BLOCK, + AGENT_FLYOUT, + CREATE_FLEET_SERVER_POLICY_BTN, + AGENT_POLICY_CREATE_STATUS_CALLOUT, + FLEET_SERVER_HOST_INPUT, + ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON, + ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON, } from '../screens/fleet'; import { cleanupAgentPolicies, unenrollAgent } from '../tasks/cleanup'; import { verifyPolicy, verifyAgentPackage, navigateToTab } from '../tasks/fleet'; @@ -49,11 +52,11 @@ describe('Fleet startup', () => { it('should create agent policy', () => { cy.getBySel(ADD_AGENT_BUTTON_TOP).click(); - cy.getBySel(STANDALONE_TAB).click(); + cy.getBySel(AGENT_FLYOUT.STANDALONE_TAB).click(); cy.intercept('POST', '/api/fleet/agent_policies?sys_monitoring=true').as('createAgentPolicy'); - cy.getBySel('createPolicyBtn').click(); + cy.getBySel(AGENT_FLYOUT.CREATE_POLICY_BUTTON, { timeout: 10000 }).click(); let agentPolicyId: string; const startTime = Date.now(); @@ -62,12 +65,12 @@ describe('Fleet startup', () => { agentPolicyId = xhr.response.body.item.id; // verify create button changed to dropdown - cy.getBySel('agentPolicyDropdown'); + cy.getBySel(AGENT_FLYOUT.POLICY_DROPDOWN); // verify agent.yml code block has new policy id - cy.getBySel(AGENT_POLICY_CODE_BLOCK).contains(`id: ${agentPolicyId}`); + cy.getBySel(AGENT_FLYOUT.AGENT_POLICY_CODE_BLOCK).contains(`id: ${agentPolicyId}`); - cy.getBySel(AGENT_FLYOUT_CLOSE_BUTTON).click(); + cy.getBySel(AGENT_FLYOUT.CLOSE_BUTTON).click(); // verify policy is created and has system package verifyPolicy('Agent policy 1', ['System']); @@ -77,11 +80,11 @@ describe('Fleet startup', () => { }); it('should create Fleet Server policy', () => { - cy.getBySel('fleetServerFlyoutTab-advanced').click(); - cy.getBySel('createFleetServerPolicyBtn').click(); + cy.getBySel(AGENT_FLYOUT.ADVANCED_TAB_BUTTON).click(); + cy.getBySel(CREATE_FLEET_SERVER_POLICY_BTN).click(); // Wait until the success callout is shown before navigating away - cy.getBySel('agentPolicyCreateStatusCallOut') + cy.getBySel(AGENT_POLICY_CREATE_STATUS_CALLOUT) .should('exist') .and('have.class', 'euiCallOut--success'); @@ -89,18 +92,18 @@ describe('Fleet startup', () => { verifyPolicy('Fleet Server policy 1', ['Fleet Server', 'System']); navigateToTab(AGENTS_TAB); - cy.getBySel('fleetServerFlyoutTab-advanced').click(); + cy.getBySel(AGENT_FLYOUT.ADVANCED_TAB_BUTTON).click(); // verify create button changed to dropdown - cy.getBySel('agentPolicyDropdown'); + cy.getBySel(AGENT_FLYOUT.POLICY_DROPDOWN); // verify fleet server enroll command contains created policy id - cy.getBySel('fleetServerHostInput') + cy.getBySel(FLEET_SERVER_HOST_INPUT) .getBySel('comboBoxSearchInput') .type('https://localhost:8220'); - cy.getBySel('fleetServerAddHostBtn').click(); - cy.getBySel('fleetServerGenerateServiceTokenBtn').click(); + cy.getBySel(ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON).click(); + cy.getBySel(ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON).click(); cy.get('.euiCodeBlock__code').contains('--fleet-server-policy=fleet-server-policy'); }); }); diff --git a/x-pack/plugins/fleet/cypress/integration/install_assets.spec.ts b/x-pack/plugins/fleet/cypress/integration/install_assets.spec.ts index 1c6268d2cf66a..81ef56a4b1f52 100644 --- a/x-pack/plugins/fleet/cypress/integration/install_assets.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/install_assets.spec.ts @@ -7,6 +7,9 @@ import type { Interception } from 'cypress/types/net-stubbing'; +import { CONFIRM_MODAL } from '../screens/navigation'; +import { SETTINGS } from '../screens/integrations'; + describe('Install unverified package assets', () => { beforeEach(() => { cy.intercept('POST', '/api/fleet/epm/packages/fleet_server/*', (req) => { @@ -46,13 +49,13 @@ describe('Install unverified package assets', () => { it('should show force install modal if package is unverified', () => { cy.visit('app/integrations/detail/fleet_server/settings'); - cy.getBySel('installAssetsButton').click(); + cy.getBySel(SETTINGS.INSTALL_ASSETS_BTN).click(); // this action will install x assets modal - const confirmInstall = cy.getBySel('confirmModalConfirmButton'); + const confirmInstall = cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON); confirmInstall.click(); // unverified integration force install modal - const installAnyway = cy.getBySel('confirmModalConfirmButton').contains('Install anyway'); + const installAnyway = cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).contains('Install anyway'); installAnyway.click(); // cypress 'hack' to get all requests made to an intercepted request diff --git a/x-pack/plugins/fleet/cypress/integration/integrations_mock.spec.ts b/x-pack/plugins/fleet/cypress/integration/integrations_mock.spec.ts index 1b969e1a8ca2e..ce207cd3598e2 100644 --- a/x-pack/plugins/fleet/cypress/integration/integrations_mock.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/integrations_mock.spec.ts @@ -7,7 +7,9 @@ import { navigateTo } from '../tasks/navigation'; import { UPDATE_PACKAGE_BTN } from '../screens/integrations'; +import { LOADING_SPINNER, TOAST_CLOSE_BTN } from '../screens/navigation'; import { AGENT_POLICY_SAVE_INTEGRATION } from '../screens/fleet'; +import { INSTALLED_VERSION, INTEGRATION_POLICIES_UPGRADE_CHECKBOX } from '../screens/integrations'; describe('Add Integration - Mock API', () => { describe('upgrade package and upgrade package policy', () => { @@ -92,10 +94,10 @@ describe('Add Integration - Mock API', () => { it('should upgrade policies without integration update', () => { navigateTo(`app/integrations/detail/apache-${oldVersion}/settings`); - cy.get('.euiLoadingSpinner').should('not.exist'); - cy.getBySel('installedVersion').contains(oldVersion); + cy.getBySel(LOADING_SPINNER).should('not.exist'); + cy.getBySel(INSTALLED_VERSION).contains(oldVersion); - cy.get('#upgradePoliciesCheckbox').uncheck({ force: true }); + cy.getBySel(INTEGRATION_POLICIES_UPGRADE_CHECKBOX).uncheck({ force: true }); cy.intercept(`/api/fleet/epm/packages/apache/${newVersion}`, { item: { @@ -109,8 +111,8 @@ describe('Add Integration - Mock API', () => { }).as('updatePackage'); cy.getBySel(UPDATE_PACKAGE_BTN).click(); cy.wait('@updatePackage'); - cy.get('#upgradePoliciesCheckbox').should('not.exist'); - cy.getBySel('installedVersion').contains(newVersion); + cy.getBySel(INTEGRATION_POLICIES_UPGRADE_CHECKBOX).should('not.exist'); + cy.getBySel(INSTALLED_VERSION).contains(newVersion); }); it('should upgrade integration policy', () => { @@ -141,7 +143,7 @@ describe('Add Integration - Mock API', () => { '/app/fleet/policies/package-1/upgrade-package-policy/apache-2?from=integrations-policy-list' ); - cy.getBySel('toastCloseButton').click(); + cy.getBySel(TOAST_CLOSE_BTN).click(); cy.getBySel(AGENT_POLICY_SAVE_INTEGRATION).click(); cy.wait('@updateApachePolicy').then((interception) => { diff --git a/x-pack/plugins/fleet/cypress/integration/integrations_real.spec.ts b/x-pack/plugins/fleet/cypress/integration/integrations_real.spec.ts index 1caa90ba1b785..34fa5b7af55ca 100644 --- a/x-pack/plugins/fleet/cypress/integration/integrations_real.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/integrations_real.spec.ts @@ -14,9 +14,8 @@ import { } from '../tasks/integrations'; import { AGENT_POLICY_NAME_LINK, - CONFIRM_MODAL_BTN, FLYOUT_CLOSE_BTN_SEL, - INTEGRATIONS_CARD, + getIntegrationCard, INTEGRATION_NAME_LINK, LATEST_VERSION, PACKAGE_VERSION, @@ -24,30 +23,22 @@ import { SETTINGS_TAB, UPDATE_PACKAGE_BTN, INTEGRATIONS_SEARCHBAR_INPUT, + SETTINGS, + INTEGRATION_POLICIES_UPGRADE_CHECKBOX, } from '../screens/integrations'; +import { LOADING_SPINNER, CONFIRM_MODAL } from '../screens/navigation'; import { ADD_PACKAGE_POLICY_BTN } from '../screens/fleet'; import { cleanupAgentPolicies } from '../tasks/cleanup'; -describe.skip('Add Integration - Real API', () => { - const integration = 'Apache'; +describe('Add Integration - Real API', () => { + const integration = 'apache'; after(() => { - cleanupAgentPolicies(); + deleteIntegrations(); }); - it('should install integration without policy', () => { - cy.visit('/app/integrations/detail/tomcat/settings'); - - cy.get('.euiButton').contains('Install Apache Tomcat assets').click(); - cy.get('.euiCallOut').contains('This action will install 1 assets'); - cy.getBySel(CONFIRM_MODAL_BTN).click(); - - cy.get('.euiLoadingSpinner').should('not.exist'); - - cy.get('.euiButton').contains('Uninstall Apache Tomcat').click(); - cy.getBySel(CONFIRM_MODAL_BTN).click(); - cy.get('.euiLoadingSpinner').should('not.exist'); - cy.get('.euiButton').contains('Install Apache Tomcat assets'); + afterEach(() => { + cleanupAgentPolicies(); }); function addAndVerifyIntegration() { @@ -66,27 +57,49 @@ describe.skip('Add Integration - Real API', () => { navigateTo(INTEGRATIONS); cy.wait('@packages'); - cy.get('.euiLoadingSpinner').should('not.exist'); - cy.get(INTEGRATIONS_SEARCHBAR_INPUT).type('Apache'); - cy.get(INTEGRATIONS_CARD).contains(integration).click(); + cy.getBySel(LOADING_SPINNER).should('not.exist'); + cy.getBySel(INTEGRATIONS_SEARCHBAR_INPUT).type('Apache'); + cy.getBySel(getIntegrationCard(integration)).click(); addIntegration(); cy.getBySel(INTEGRATION_NAME_LINK).contains('apache-1'); } - afterEach(() => { - deleteIntegrations(integration); + it('should install integration without policy', () => { + cy.visit('/app/integrations/detail/tomcat/settings'); + + cy.getBySel(SETTINGS.INSTALL_ASSETS_BTN).click(); + cy.get('.euiCallOut').contains('This action will install 1 assets'); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); + + cy.getBySel(LOADING_SPINNER).should('not.exist'); + + cy.getBySel(SETTINGS.UNINSTALL_ASSETS_BTN).click(); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); + cy.getBySel(LOADING_SPINNER).should('not.exist'); + cy.getBySel(SETTINGS.INSTALL_ASSETS_BTN).should('exist'); }); + it('should display Apache integration in the Policies list once installed ', () => { addAndVerifyIntegration(); cy.getBySel(AGENT_POLICY_NAME_LINK).contains('Agent policy 1'); }); it('should add integration to policy', () => { - cy.request('/api/fleet/agent_policies').then((response: any) => { - const agentPolicyId = response.body.items - .filter((policy: any) => policy.name === 'Agent policy 1') - .map((policy: any) => policy.id); + const agentPolicyId = 'policy_1'; + cy.request({ + method: 'POST', + url: `/api/fleet/agent_policies`, + body: { + id: `${agentPolicyId}`, + name: 'Agent policy 1', + description: 'desc', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + }, + headers: { 'kbn-xsrf': 'cypress' }, + }); + cy.request('/api/fleet/agent_policies').then((response: any) => { cy.visit(`/app/fleet/policies/${agentPolicyId}`); cy.intercept( @@ -104,9 +117,9 @@ describe.skip('Add Integration - Real API', () => { cy.getBySel(ADD_PACKAGE_POLICY_BTN).click(); cy.wait('@packages'); - cy.get('.euiLoadingSpinner').should('not.exist'); - cy.get(INTEGRATIONS_SEARCHBAR_INPUT).type('Apache'); - cy.get(INTEGRATIONS_CARD).contains(integration).click(); + cy.getBySel(LOADING_SPINNER).should('not.exist'); + cy.getBySel(INTEGRATIONS_SEARCHBAR_INPUT).type('Apache'); + cy.getBySel(getIntegrationCard(integration)).click(); addIntegration({ useExistingPolicy: true }); cy.get('.euiBasicTable-loading').should('not.exist'); cy.get('.euiTitle').contains('Agent policy 1'); @@ -120,7 +133,7 @@ describe.skip('Add Integration - Real API', () => { installPackageWithVersion('apache', oldVersion); navigateTo(`app/integrations/detail/apache-${oldVersion}/policies`); - addIntegration({ useExistingPolicy: true }); + addIntegration(); cy.getBySel(INTEGRATION_NAME_LINK).contains('apache-'); cy.getBySel(PACKAGE_VERSION).contains(oldVersion); @@ -129,11 +142,11 @@ describe.skip('Add Integration - Real API', () => { cy.getBySel(SETTINGS_TAB).click(); cy.getBySel(UPDATE_PACKAGE_BTN).click(); - cy.getBySel(CONFIRM_MODAL_BTN).click(); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); cy.getBySel(LATEST_VERSION).then(($title) => { const newVersion = $title.text(); - cy.get('#upgradePoliciesCheckbox').should('not.exist'); + cy.getBySel(INTEGRATION_POLICIES_UPGRADE_CHECKBOX).should('not.exist'); cy.getBySel(POLICIES_TAB).click(); cy.getBySel(PACKAGE_VERSION).contains(oldVersion).should('not.exist'); cy.getBySel(PACKAGE_VERSION).contains(newVersion); diff --git a/x-pack/plugins/fleet/cypress/integration/package_policy.spec.ts b/x-pack/plugins/fleet/cypress/integration/package_policy.spec.ts index b2fa21371539d..8b1569574c4e7 100644 --- a/x-pack/plugins/fleet/cypress/integration/package_policy.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/package_policy.spec.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { TOAST_CLOSE_BTN } from '../screens/navigation'; describe('Edit package policy', () => { const policyConfig = { @@ -109,7 +110,7 @@ describe('Edit package policy', () => { it('should edit package policy', () => { cy.visit('/app/fleet/policies/fleet-server-policy/edit-integration/policy-1'); - cy.getBySel('toastCloseButton').click(); + cy.getBySel(TOAST_CLOSE_BTN).click(); cy.getBySel('packagePolicyDescriptionInput').clear().type('desc'); cy.intercept('PUT', '/api/fleet/package_policies/policy-1', { diff --git a/x-pack/plugins/fleet/cypress/integration/privileges_fleet_all_integrations_read.spec.ts b/x-pack/plugins/fleet/cypress/integration/privileges_fleet_all_integrations_read.spec.ts index 327ba39e65377..517687a0bcd19 100644 --- a/x-pack/plugins/fleet/cypress/integration/privileges_fleet_all_integrations_read.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/privileges_fleet_all_integrations_read.spec.ts @@ -15,6 +15,7 @@ import { import { loginWithUserAndWaitForPage, logout } from '../tasks/login'; import { navigateToTab, createAgentPolicy } from '../tasks/fleet'; import { cleanupAgentPolicies, unenrollAgent } from '../tasks/cleanup'; +import { getIntegrationCard } from '../screens/integrations'; import { FLEET_SERVER_MISSING_PRIVILEGES_TITLE, @@ -24,7 +25,7 @@ import { AGENT_POLICY_SAVE_INTEGRATION, ADD_PACKAGE_POLICY_BTN, } from '../screens/fleet'; -import { ADD_POLICY_BTN, AGENT_POLICY_NAME_LINK } from '../screens/integrations'; +import { ADD_INTEGRATION_POLICY_BTN, AGENT_POLICY_NAME_LINK } from '../screens/integrations'; const rolesToCreate = [FleetAllIntegrReadRole]; const usersToCreate = [FleetAllIntegrReadUser]; @@ -81,8 +82,8 @@ describe('When the user has All privilege for Fleet but Read for integrations', describe('Integrations', () => { it('are visible but cannot be added', () => { loginWithUserAndWaitForPage(INTEGRATIONS, FleetAllIntegrReadUser); - cy.getBySel('integration-card:epr:apache').click(); - cy.getBySel(ADD_POLICY_BTN).should('be.disabled'); + cy.getBySel(getIntegrationCard('apache')).click(); + cy.getBySel(ADD_INTEGRATION_POLICY_BTN).should('be.disabled'); }); }); }); diff --git a/x-pack/plugins/fleet/cypress/integration/privileges_fleet_none_integrations_all.spec.ts b/x-pack/plugins/fleet/cypress/integration/privileges_fleet_none_integrations_all.spec.ts index 68fcecb76de21..71e7d948f928a 100644 --- a/x-pack/plugins/fleet/cypress/integration/privileges_fleet_none_integrations_all.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/privileges_fleet_none_integrations_all.spec.ts @@ -14,7 +14,7 @@ import { } from '../tasks/privileges'; import { loginWithUserAndWaitForPage, logout } from '../tasks/login'; -import { ADD_POLICY_BTN } from '../screens/integrations'; +import { ADD_INTEGRATION_POLICY_BTN, getIntegrationCard } from '../screens/integrations'; const rolesToCreate = [FleetNoneIntegrAllRole]; const usersToCreate = [FleetNoneIntegrAllUser]; @@ -34,7 +34,7 @@ describe('When the user has All privileges for Integrations but None for for Fle it('Integrations are visible but cannot be added', () => { loginWithUserAndWaitForPage(INTEGRATIONS, FleetNoneIntegrAllUser); - cy.getBySel('integration-card:epr:apache').click(); - cy.getBySel(ADD_POLICY_BTN).should('be.disabled'); + cy.getBySel(getIntegrationCard('apache')).click(); + cy.getBySel(ADD_INTEGRATION_POLICY_BTN).should('be.disabled'); }); }); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index 72dd6486c6843..a9df1dc4d8ef1 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -7,16 +7,13 @@ export const ADD_AGENT_BUTTON = 'addAgentButton'; export const ADD_AGENT_BUTTON_TOP = 'addAgentBtnTop'; -export const CREATE_POLICY_BUTTON = 'createPolicyBtn'; -export const AGENT_FLYOUT_CLOSE_BUTTON = 'euiFlyoutCloseButton'; -export const AGENT_POLICY_CODE_BLOCK = 'agentPolicyCodeBlock'; export const AGENTS_TAB = 'fleet-agents-tab'; export const AGENT_POLICIES_TAB = 'fleet-agent-policies-tab'; export const ENROLLMENT_TOKENS_TAB = 'fleet-enrollment-tokens-tab'; export const DATA_STREAMS_TAB = 'fleet-datastreams-tab'; export const SETTINGS_TAB = 'fleet-settings-tab'; -export const STANDALONE_TAB = 'standaloneTab'; + export const MISSING_PRIVILEGES_TITLE = 'missingPrivilegesPromptTitle'; export const MISSING_PRIVILEGES_MESSAGE = 'missingPrivilegesPromptMessage'; export const FLEET_SERVER_MISSING_PRIVILEGES_MESSAGE = 'fleetServerMissingPrivilegesMessage'; @@ -26,20 +23,57 @@ export const PACKAGE_POLICY_TABLE_LINK = 'PackagePoliciesTableLink'; export const ADD_PACKAGE_POLICY_BTN = 'addPackagePolicyButton'; export const GENERATE_FLEET_SERVER_POLICY_BUTTON = 'generateFleetServerPolicyButton'; export const ADD_FLEET_SERVER_HEADER = 'addFleetServerHeader'; -export const AGENTS_QUICK_START_TAB_BUTTON = 'fleetServerFlyoutTab-quickStart'; -export const AGENTS_ADVANCED_TAB_BUTTON = 'fleetServerFlyoutTab-advanced'; + export const PLATFORM_TYPE_LINUX_BUTTON = 'platformTypeLinux'; export const ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON = 'fleetServerAddHostBtn'; export const ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON = 'fleetServerGenerateServiceTokenBtn'; -export const AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON = 'createAgentPolicyButton'; -export const AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT_TITLE = 'createAgentPolicyFlyoutTitle'; + +export const CREATE_FLEET_SERVER_POLICY_BTN = 'createFleetServerPolicyBtn'; + export const AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD = 'createAgentPolicyNameField'; export const AGENT_POLICIES_FLYOUT_ADVANCED_DEFAULT_NAMESPACE_HEADER = 'defaultNamespaceHeader'; export const AGENT_POLICY_FLYOUT_CREATE_BUTTON = 'createAgentPolicyFlyoutBtn'; -export const ENROLLMENT_TOKENS_CREATE_TOKEN_BUTTON = 'createEnrollmentTokenButton'; -export const ENROLLMENT_TOKENS_CREATE_TOKEN_NAME_FIELD = 'createEnrollmentTokenNameField'; + +export const ENROLLMENT_TOKENS = { + CREATE_TOKEN_BUTTON: 'createEnrollmentTokenButton', + CREATE_TOKEN_MODAL_NAME_FIELD: 'createEnrollmentTokenNameField', + CREATE_TOKEN_MODAL_SELECT_FIELD: 'createEnrollmentTokenSelectField', + LIST_TABLE: 'enrollmentTokenListTable', + TABLE_REVOKE_BTN: 'enrollmentTokenTable.revokeBtn', +}; export const SETTINGS_FLEET_SERVER_HOST_HEADING = 'fleetServerHostHeader'; +export const SETTINGS_SAVE_BTN = 'saveApplySettingsBtn'; + +export const AGENT_POLICY_SYSTEM_MONITORING_CHECKBOX = 'agentPolicyFormSystemMonitoringCheckbox'; +export const INSTALL_INTEGRATIONS_ADVANCE_OPTIONS_BTN = 'AgentPolicyAdvancedOptions.AccordionBtn'; +export const AGENT_POLICY_CREATE_STATUS_CALLOUT = 'agentPolicyCreateStatusCallOut'; +export const FLEET_SERVER_HOST_INPUT = 'fleetServerHostInput'; +export const EXISTING_HOSTS_TAB = 'existingHostsTab'; +export const NEW_HOSTS_TAB = 'newHostsTab'; + +export const CURRENT_BULK_UPGRADES_CALLOUT = { + ABORT_BTN: 'currentBulkUpgrade.abortBtn', +}; + +export const AGENT_FLYOUT = { + CREATE_POLICY_BUTTON: 'createPolicyBtn', + CLOSE_BUTTON: 'euiFlyoutCloseButton', + POLICY_DROPDOWN: 'agentPolicyDropdown', + QUICK_START_TAB_BUTTON: 'fleetServerFlyoutTab-quickStart', + ADVANCED_TAB_BUTTON: 'fleetServerFlyoutTab-advanced', + AGENT_POLICY_CODE_BLOCK: 'agentPolicyCodeBlock', + STANDALONE_TAB: 'standaloneTab', + CONFIRM_AGENT_ENROLLMENT_BUTTON: 'ConfirmAgentEnrollmentButton', + INCOMING_DATA_CONFIRMED_CALL_OUT: 'IncomingDataConfirmedCallOut', +}; + +export const AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT = { + TITLE: 'createAgentPolicyFlyoutTitle', + CREATE_BUTTON: 'createAgentPolicyButton', + COLLECT_LOGS_CHECKBOX: 'collectLogsCheckbox', + COLLECT_METRICS_CHECKBOX: 'collectMetricsCheckbox', +}; export const AGENT_BINARY_SOURCES_TABLE = 'AgentDownloadSourcesTable'; export const AGENT_BINARY_SOURCES_TABLE_ACTIONS = { @@ -59,13 +93,24 @@ export const AGENT_BINARY_SOURCES_FLYOUT = { CANCEL_BUTTON: 'editDownloadSourcesFlyout.cancelBtn', }; -export const ADD_AGENT_FLYOUT = { - CONFIRM_AGENT_ENROLLMENT_BUTTON: 'ConfirmAgentEnrollmentButton', - INCOMING_DATA_CONFIRMED_CALL_OUT: 'IncomingDataConfirmedCallOut', +export const SETTINGS_OUTPUTS = { + EDIT_BTN: 'editOutputBtn', + ADD_BTN: 'addOutputBtn', + EDIT_HOSTS_BTN: 'editHostsBtn', + NAME_INPUT: 'settingsOutputsFlyout.nameInput', + TYPE_INPUT: 'settingsOutputsFlyout.typeInput', }; -export const CONFIRM_MODAL_CONFIRM_BUTTON = 'confirmModalConfirmButton'; -export const CONFIRM_MODAL_CANCEL_BUTTON = 'confirmModalCancelButton'; export const AGENT_POLICY_FORM = { DOWNLOAD_SOURCE_SELECT: 'agentPolicyForm.downloadSource.select', }; + +export const FLEET_AGENT_LIST_PAGE = { + TABLE: 'fleetAgentListTable', + STATUS_FILTER: 'agentList.statusFilter', + POLICY_FILTER: 'agentList.policyFilter', + QUERY_INPUT: 'agentList.queryInput', + SHOW_UPGRADEABLE: 'agentList.showUpgradeable', + CHECKBOX_SELECT_ALL: 'checkboxSelectAll', + BULK_ACTIONS_BUTTON: 'agentBulkActionsButton', +}; diff --git a/x-pack/plugins/fleet/cypress/screens/integrations.ts b/x-pack/plugins/fleet/cypress/screens/integrations.ts index 929e36d191230..3915c6600baaa 100644 --- a/x-pack/plugins/fleet/cypress/screens/integrations.ts +++ b/x-pack/plugins/fleet/cypress/screens/integrations.ts @@ -5,17 +5,13 @@ * 2.0. */ -export const ADD_POLICY_BTN = 'addIntegrationPolicyButton'; +export const ADD_INTEGRATION_POLICY_BTN = 'addIntegrationPolicyButton'; export const CREATE_PACKAGE_POLICY_SAVE_BTN = 'createPackagePolicySaveButton'; -export const INTEGRATIONS_CARD = '.euiCard__titleButton'; export const INTEGRATION_NAME_LINK = 'integrationNameLink'; export const AGENT_POLICY_NAME_LINK = 'agentPolicyNameLink'; export const AGENT_ACTIONS_BTN = 'agentActionsBtn'; -export const CONFIRM_MODAL_BTN = 'confirmModalConfirmButton'; -export const CONFIRM_MODAL_BTN_SEL = `[data-test-subj=${CONFIRM_MODAL_BTN}]`; - export const FLYOUT_CLOSE_BTN_SEL = '[data-test-subj="euiFlyoutCloseButton"]'; export const SETTINGS_TAB = 'tab-settings'; @@ -23,7 +19,17 @@ export const POLICIES_TAB = 'tab-policies'; export const ADVANCED_TAB = 'tab-custom'; export const UPDATE_PACKAGE_BTN = 'updatePackageBtn'; -export const LATEST_VERSION = 'latestVersion'; +export const LATEST_VERSION = 'epmSettings.latestVersionTitle'; +export const INSTALLED_VERSION = 'epmSettings.installedVersionTitle'; export const PACKAGE_VERSION = 'packageVersionText'; export const INTEGRATIONS_SEARCHBAR_INPUT = 'epmList.searchBar'; + +export const SETTINGS = { + INSTALL_ASSETS_BTN: 'installAssetsButton', + UNINSTALL_ASSETS_BTN: 'uninstallAssetsButton', +}; + +export const INTEGRATION_POLICIES_UPGRADE_CHECKBOX = 'epmDetails.upgradePoliciesCheckbox'; + +export const getIntegrationCard = (integration: string) => `integration-card:epr:${integration}`; diff --git a/x-pack/plugins/fleet/cypress/screens/navigation.ts b/x-pack/plugins/fleet/cypress/screens/navigation.ts index 76b73711db495..b8f6bea719e8a 100644 --- a/x-pack/plugins/fleet/cypress/screens/navigation.ts +++ b/x-pack/plugins/fleet/cypress/screens/navigation.ts @@ -7,3 +7,13 @@ export const TOGGLE_NAVIGATION_BTN = 'toggleNavButton'; export const NAV_APP_LINK = 'collapsibleNavAppLink'; +export const LOADING_SPINNER = '.euiLoadingSpinner'; +export const TOAST_CLOSE_BTN = 'toastCloseButton'; + +// these selectors are part of a common component and so are used everywhere +export const CONFIRM_MODAL = { + CONFIRM_BUTTON: 'confirmModalConfirmButton', + CANCEL_BUTTON: 'confirmModalCancelButton', +}; + +export const CONFIRM_MODAL_BTN_SEL = `[data-test-subj=${CONFIRM_MODAL.CONFIRM_BUTTON}]`; diff --git a/x-pack/plugins/fleet/cypress/tasks/cleanup.ts b/x-pack/plugins/fleet/cypress/tasks/cleanup.ts index b0d18fa9cdceb..2a1e57271f08a 100644 --- a/x-pack/plugins/fleet/cypress/tasks/cleanup.ts +++ b/x-pack/plugins/fleet/cypress/tasks/cleanup.ts @@ -48,3 +48,18 @@ export function cleanupDownloadSources() { }); }); } + +export function deleteFleetServerDocs(ignoreUnavailable: boolean = false) { + cy.task('deleteDocsByQuery', { + index: '.fleet-servers', + query: { match_all: {} }, + ignoreUnavailable, + }); +} +export function deleteAgentDocs(ignoreUnavailable: boolean = false) { + cy.task('deleteDocsByQuery', { + index: '.fleet-agents', + query: { match_all: {} }, + ignoreUnavailable, + }); +} diff --git a/x-pack/plugins/fleet/cypress/tasks/fleet.ts b/x-pack/plugins/fleet/cypress/tasks/fleet.ts index 4a23daf7a8380..35d6c5cf5dd79 100644 --- a/x-pack/plugins/fleet/cypress/tasks/fleet.ts +++ b/x-pack/plugins/fleet/cypress/tasks/fleet.ts @@ -6,13 +6,14 @@ */ import { + AGENT_FLYOUT, AGENT_POLICIES_TAB, ENROLLMENT_TOKENS_TAB, ADD_AGENT_BUTTON_TOP, - CREATE_POLICY_BUTTON, - AGENT_FLYOUT_CLOSE_BUTTON, - STANDALONE_TAB, + PACKAGE_POLICY_TABLE_LINK, } from '../screens/fleet'; +import { LOADING_SPINNER } from '../screens/navigation'; +import { getIntegrationCard } from '../screens/integrations'; export function createAgentPolicy() { cy.intercept({ @@ -20,11 +21,11 @@ export function createAgentPolicy() { method: 'POST', }).as('postAgentPolicy'); cy.getBySel(ADD_AGENT_BUTTON_TOP).click(); - cy.getBySel(STANDALONE_TAB).click(); - cy.getBySel(CREATE_POLICY_BUTTON).click(); + cy.getBySel(AGENT_FLYOUT.STANDALONE_TAB).click(); + cy.getBySel(AGENT_FLYOUT.CREATE_POLICY_BUTTON).click(); cy.wait('@postAgentPolicy'); - cy.getBySel(AGENT_FLYOUT_CLOSE_BUTTON).click(); + cy.getBySel(AGENT_FLYOUT.CLOSE_BUTTON).click(); } export function navigateToTab(tab: string) { @@ -34,7 +35,7 @@ export function navigateToTab(tab: string) { export function navigateToAgentPolicy(name: string) { cy.get('.euiLink').contains(name).click(); - cy.get('.euiLoadingSpinner').should('not.exist'); + cy.getBySel(LOADING_SPINNER).should('not.exist'); } export function navigateToEnrollmentTokens() { @@ -48,7 +49,7 @@ export function verifyPolicy(name: string, integrations: string[]) { navigateToAgentPolicy(name); integrations.forEach((integration) => { - cy.get('.euiLink').contains(integration); + cy.getBySel(PACKAGE_POLICY_TABLE_LINK).contains(integration); }); cy.get('.euiButtonEmpty').contains('View all agent policies').click(); @@ -60,16 +61,5 @@ export function verifyPolicy(name: string, integrations: string[]) { export function verifyAgentPackage() { cy.visit('/app/integrations/installed'); - cy.getBySel('integration-card:epr:elastic_agent'); -} - -export function setFleetServerHost(host = 'https://fleetserver:8220') { - cy.request({ - method: 'PUT', - url: '/api/fleet/settings', - headers: { 'kbn-xsrf': 'xx' }, - body: { - fleet_server_hosts: [host], - }, - }); + cy.getBySel(getIntegrationCard('elastic_agent')); } diff --git a/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts b/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts new file mode 100644 index 0000000000000..946ded57e738f --- /dev/null +++ b/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts @@ -0,0 +1,61 @@ +/* + * Copyright 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 { createAgentDoc } from './agents'; + +const FLEET_SERVER_POLICY_ID = 'fleet-server-policy'; + +// Create a Fleet server policy +export function setupFleetServer() { + let policyId: string; + let kibanaVersion: string; + + cy.request({ + method: 'POST', + url: '/api/fleet/agent_policies', + headers: { 'kbn-xsrf': 'xx' }, + body: { + id: FLEET_SERVER_POLICY_ID, + name: 'Fleet Server policy', + namespace: 'default', + has_fleet_server: true, + }, + }).then((response: any) => { + policyId = response.body.item.id; + }); + + cy.getKibanaVersion().then((version) => { + kibanaVersion = version; + }); + + // setup Fleet server + cy.wrap(null).then(() => { + cy.task('insertDocs', { + index: '.fleet-agents', + docs: [createAgentDoc('fleet-server', policyId, 'online', kibanaVersion)], + }); + cy.task('insertDocs', { + index: '.fleet-servers', + docs: [ + { + '@timestamp': new Date().toISOString(), + }, + ], + }); + setFleetServerHost(); + }); +} + +export function setFleetServerHost(host = 'https://fleetserver:8220') { + cy.request({ + method: 'PUT', + url: '/api/fleet/settings', + headers: { 'kbn-xsrf': 'xx' }, + body: { + fleet_server_hosts: [host], + }, + }); +} diff --git a/x-pack/plugins/fleet/cypress/tasks/integrations.ts b/x-pack/plugins/fleet/cypress/tasks/integrations.ts index 7e266dce523d5..71a8c3cd2f9a7 100644 --- a/x-pack/plugins/fleet/cypress/tasks/integrations.ts +++ b/x-pack/plugins/fleet/cypress/tasks/integrations.ts @@ -6,27 +6,34 @@ */ import { - ADD_POLICY_BTN, - CONFIRM_MODAL_BTN, + ADD_INTEGRATION_POLICY_BTN, CREATE_PACKAGE_POLICY_SAVE_BTN, FLYOUT_CLOSE_BTN_SEL, } from '../screens/integrations'; +import { AGENT_POLICY_SYSTEM_MONITORING_CHECKBOX, EXISTING_HOSTS_TAB } from '../screens/fleet'; +import { TOAST_CLOSE_BTN, CONFIRM_MODAL } from '../screens/navigation'; + export const addIntegration = ({ useExistingPolicy } = { useExistingPolicy: false }) => { - cy.getBySel(ADD_POLICY_BTN).click(); + cy.getBySel(ADD_INTEGRATION_POLICY_BTN).click(); if (useExistingPolicy) { - cy.get('#existing').click(); + cy.getBySel(EXISTING_HOSTS_TAB).click(); } else { // speeding up creating with unchecking system and agent integration - cy.getBySel('agentPolicyFormSystemMonitoringCheckbox').uncheck({ force: true }); - cy.getBySel('advancedOptionsBtn').find('.euiAccordion__button').click(); - cy.get('*[id^="logs_"]').uncheck({ force: true }); - cy.get('*[id^="metrics_"]').uncheck({ force: true }); + cy.getBySel(AGENT_POLICY_SYSTEM_MONITORING_CHECKBOX).uncheck({ force: true }); + cy.get('.euiAccordion__button').click(); + + cy.get('*[id^="logs_"]').uncheck({ + force: true, + }); + cy.get('*[id^="metrics_"]').uncheck({ + force: true, + }); } - cy.getBySel('toastCloseButton').click(); + cy.getBySel(TOAST_CLOSE_BTN).click(); cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click(); // sometimes agent is assigned to default policy, sometimes not - cy.getBySel(CONFIRM_MODAL_BTN).click(); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).should('not.exist'); clickIfVisible(FLYOUT_CLOSE_BTN_SEL); @@ -40,14 +47,14 @@ export function clickIfVisible(selector: string) { }); } -export const deleteIntegrations = async (integration: string) => { +export const deleteIntegrations = async () => { const ids: string[] = []; cy.request('/api/fleet/package_policies').then((response: any) => { response.body.items.forEach((policy: any) => ids.push(policy.id)); cy.request({ url: `/api/fleet/package_policies/delete`, headers: { 'kbn-xsrf': 'cypress' }, - body: `{ "packagePolicyIds": ${JSON.stringify(ids)} }`, + body: `{ "packagePolicyIds": ${JSON.stringify(ids)}, "force": true }`, method: 'POST', }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx index d9e44c6207fc4..f8eab1fe8513e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx @@ -182,6 +182,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = options={[ { id: `${dataTypes.Logs}_${monitoringCheckboxIdSuffix}`, + 'data-test-subj': 'collectLogsCheckbox', label: ( <> = }, { id: `${dataTypes.Metrics}_${monitoringCheckboxIdSuffix}`, + 'data-test-subj': 'collectMetricsCheckbox', label: ( <> = ({ const tabs = [ { id: SelectedPolicyTab.NEW, + 'data-test-subj': 'newHostsTab', name: 'New hosts', content: ( = ({ }, { id: SelectedPolicyTab.EXISTING, + 'data-test-subj': 'existingHostsTab', name: 'Existing hosts', content: ( { = () => { + data-test-subj="enrollmentTokenListTable" loading={isLoading} hasActions={true} noItemsMessage={ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 3d0e9b2801394..02e476ce1ed3d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -116,6 +116,7 @@ export const EditOutputFlyout: React.FunctionComponent = {...inputs.nameInput.formRowProps} > = > = )} {isESOutput && ( = memo(({ packageInfo, theme$ }: Prop /> - + {installedVersion} @@ -267,7 +267,7 @@ export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Prop /> - + {latestVersion} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx index 73780e23a1a7e..df472c765c09a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx @@ -58,6 +58,7 @@ export const UninstallButton: React.FunctionComponent = ({ return canRemovePackages ? ( <> setIsUninstallModalVisible(true)} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx index cc11dd6819695..526c50aca1b96 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx @@ -326,6 +326,7 @@ export const UpdateButton: React.FunctionComponent = ({ }, }} id="upgradePoliciesCheckbox" + data-test-subj="epmDetails.upgradePoliciesCheckbox" disabled={!canUpgradePackages} checked={upgradePackagePolicies} onChange={handleUpgradePackagePoliciesChange} diff --git a/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx b/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx index c611c9a543d73..8689e920d2a0f 100644 --- a/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx +++ b/x-pack/plugins/fleet/public/components/new_enrollment_key_modal.tsx @@ -131,7 +131,12 @@ export const NewEnrollmentTokenModal: React.FunctionComponent = ({ })} {...form.policyIdInput.formRowProps} > - + diff --git a/x-pack/plugins/fleet/public/components/package_icon.tsx b/x-pack/plugins/fleet/public/components/package_icon.tsx index 242c97da0896c..d3db0b3e6c248 100644 --- a/x-pack/plugins/fleet/public/components/package_icon.tsx +++ b/x-pack/plugins/fleet/public/components/package_icon.tsx @@ -19,7 +19,7 @@ import { usePackageIconType } from '../hooks'; // override those styles until the bug is fixed or we find a better approach const Icon = styled(EuiIcon)` width: '16px'; - margin: unset !important; + margin-block-end: unset !important; `; export const PackageIcon: React.FunctionComponent< diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 3f11900d87e75..9804f0d147868 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -106,6 +106,7 @@ async function mockedGetInstallation(params: any) { let pkg; if (params.pkgName === 'apache') pkg = { version: '1.3.2' }; if (params.pkgName === 'aws') pkg = { version: '0.3.3' }; + if (params.pkgName === 'endpoint') pkg = { version: '1.0.0' }; return Promise.resolve(pkg); } @@ -113,7 +114,7 @@ async function mockedGetPackageInfo(params: any) { let pkg; if (params.pkgName === 'apache') pkg = { version: '1.3.2' }; if (params.pkgName === 'aws') pkg = { version: '0.3.3' }; - if (params.pkgName === 'endpoint') pkg = {}; + if (params.pkgName === 'endpoint') pkg = { version: '1.0.0' }; if (params.pkgName === 'test') { pkg = { version: '1.0.2', @@ -3463,6 +3464,36 @@ describe('getUpgradeDryRunDiff', () => { expect(res.hasErrors).toBeTruthy(); }); + + it('should return no errors if upgrading 2 package policies', async () => { + savedObjectsClient.get.mockImplementation((type, id) => + Promise.resolve({ + id, + type: 'abcd', + references: [], + version: '0.9.0', + attributes: { ...createPackagePolicyMock(), name: id }, + }) + ); + const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const res = await packagePolicyService.upgrade(savedObjectsClient, elasticsearchClient, [ + 'package-policy-id', + 'package-policy-id-2', + ]); + + expect(res).toEqual([ + { + id: 'package-policy-id', + name: 'package-policy-id', + success: true, + }, + { + id: 'package-policy-id-2', + name: 'package-policy-id-2', + success: true, + }, + ]); + }); }); describe('_applyIndexPrivileges()', () => { diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 00fbec55168bb..3d481db8d63dc 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -659,19 +659,22 @@ class PackagePolicyService implements PackagePolicyServiceInterface { for (const id of ids) { try { - let packageInfo: PackageInfo; - ({ packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo( - soClient, - id, - packagePolicy, - pkgVersion - )); + const { packagePolicy: currentPackagePolicy, packageInfo } = + await this.getUpgradePackagePolicyInfo(soClient, id, packagePolicy, pkgVersion); - if (packagePolicy.is_managed && !options?.force) { + if (currentPackagePolicy.is_managed && !options?.force) { throw new PackagePolicyRestrictionRelatedError(`Cannot upgrade package policy ${id}`); } - await this.doUpgrade(soClient, esClient, id, packagePolicy!, result, packageInfo, options); + await this.doUpgrade( + soClient, + esClient, + id, + currentPackagePolicy, + result, + packageInfo, + options + ); } catch (error) { result.push({ id, diff --git a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap index 2c58f75779456..c69a3f0567a3c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap @@ -5,6 +5,9 @@ Object { "chain": Array [ Object { "arguments": Object { + "addTimeMarker": Array [ + false, + ], "emphasizeFitting": Array [ true, ], diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts index 11215a8cf492c..e5faf9c2682ad 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts @@ -8,7 +8,7 @@ import { Ast, fromExpression } from '@kbn/interpreter'; import { Position } from '@elastic/charts'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { getXyVisualization } from './xy_visualization'; +import { getXyVisualization, XYState } from './xy_visualization'; import { OperationDescriptor } from '../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { layerTypes } from '../../../common'; @@ -538,4 +538,43 @@ describe('#toExpression', () => { expect(getYConfigColorForLayer(expression, 0)).toEqual([]); expect(getYConfigColorForLayer(expression, 1)).toEqual([defaultReferenceLineColor]); }); + + it('should correctly set the current time marker visibility settings', () => { + const state: XYState = { + legend: { position: Position.Bottom, isVisible: true }, + valueLabels: 'show', + preferredSeriesType: 'bar', + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + seriesType: 'area', + splitAccessor: 'd', + xAccessor: 'a', + accessors: ['b', 'c'], + }, + ], + }; + let expression = xyVisualization.toExpression( + { + ...state, + showCurrentTimeMarker: true, + }, + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers + ) as Ast; + expect(expression.chain[0].arguments.addTimeMarker[0] as Ast).toEqual(true); + + expression = xyVisualization.toExpression( + { + ...state, + showCurrentTimeMarker: false, + }, + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers + ) as Ast; + expect(expression.chain[0].arguments.addTimeMarker[0] as Ast).toEqual(false); + }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 167811a7f8691..387542e5465f8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -300,6 +300,7 @@ export const buildExpression = ( fillOpacity: [state.fillOpacity || 0.3], valueLabels: [state?.valueLabels || 'hide'], hideEndzones: [state?.hideEndzones || false], + addTimeMarker: [state?.showCurrentTimeMarker || false], valuesInLegend: [state?.valuesInLegend || false], yAxisConfigs: [...yAxisConfigsToExpression(yAxisConfigs)], xAxisConfig: [ diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 8e140b83cb552..27c5d485065c8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -154,6 +154,7 @@ export interface XYState { curveType?: XYCurveType; fillOpacity?: number; hideEndzones?: boolean; + showCurrentTimeMarker?: boolean; valuesInLegend?: boolean; } diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.test.tsx index 3589e0ff23d80..481987520ea23 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.test.tsx @@ -116,6 +116,28 @@ describe('Axes Settings', () => { expect(component.find('[data-test-subj="lnsshowEndzones"]').prop('checked')).toBe(true); }); + it('hides the current time marker visibility flag if no setter is passed in', () => { + const component = shallow(); + expect(component.find('[data-test-subj="lnsshowCurrentTimeMarker"]')).toHaveLength(0); + }); + + it('shows the current time marker switch if setter is present', () => { + const mockToggle = jest.fn(); + const component = shallow( + + ); + const switchElement = component.find('[data-test-subj="lnsshowCurrentTimeMarker"]'); + expect(switchElement.prop('checked')).toBe(false); + + switchElement.simulate('change'); + + expect(mockToggle).toHaveBeenCalledWith(true); + }); + describe('axis extent', () => { it('hides the extent section if no extent is passed in', () => { const component = shallow(); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx index cb02bb796a3aa..b765d30a0d8cf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx @@ -99,6 +99,14 @@ export interface AxisSettingsPopoverProps { * Flag whether endzones are visible */ endzonesVisible?: boolean; + /** + * Set current time marker visibility + */ + setCurrentTimeMarkerVisibility?: (checked: boolean) => void; + /** + * Flag whether current time marker is visible + */ + currentTimeMarkerVisible?: boolean; /** * Set scale */ @@ -222,6 +230,8 @@ export const AxisSettingsPopover: React.FunctionComponent )} + {setCurrentTimeMarkerVisibility && ( + + setCurrentTimeMarkerVisibility(!Boolean(currentTimeMarkerVisible))} + checked={Boolean(currentTimeMarkerVisible)} + showLabel={false} + /> + + )} {setScale && ( layer.xAccessor && getScaleType( props.frame.datasourceLayers[layer.layerId]?.getOperationForColumnId(layer.xAccessor) ?? null, ScaleType.Linear - ) === 'time' - ) + ) === ScaleType.Time + ); + + // only allow changing endzone visibility if it could show up theoretically (if it's a time viz) + const onChangeEndzoneVisiblity = isTimeVis ? (checked: boolean): void => { setState({ ...state, @@ -237,6 +239,15 @@ export const XyToolbar = memo(function XyToolbar( } : undefined; + const onChangeCurrentTimeMarkerVisibility = isTimeVis + ? (checked: boolean): void => { + setState({ + ...state, + showCurrentTimeMarker: checked, + }); + } + : undefined; + const legendMode = state?.legend.isVisible && !state?.legend.showSingleSeries ? 'auto' @@ -503,6 +514,8 @@ export const XyToolbar = memo(function XyToolbar( isAxisTitleVisible={axisTitlesVisibilitySettings.x} endzonesVisible={!state?.hideEndzones} setEndzoneVisibility={onChangeEndzoneVisiblity} + currentTimeMarkerVisible={state?.showCurrentTimeMarker} + setCurrentTimeMarkerVisibility={onChangeCurrentTimeMarkerVisibility} hasBarOrAreaOnAxis={false} hasPercentageAxis={false} useMultilayerTimeAxis={ diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx index 9747bec0e9db0..82e979c245df2 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx @@ -137,6 +137,37 @@ describe('XY Config panels', () => { expect(component.find(AxisSettingsPopover).at(2).prop('setEndzoneVisibility')).toBeFalsy(); }); + it('should pass in current time marker visibility setter and current state for time chart', () => { + const datasourceLayers = frame.datasourceLayers as Record; + (datasourceLayers.first.getOperationForColumnId as jest.Mock).mockReturnValue({ + dataType: 'date', + }); + const mockSetState = jest.fn(); + const stateForTest = testState(); + const state = { + ...stateForTest, + showCurrentTimeMarker: true, + layers: [ + { + ...stateForTest.layers[0], + yConfig: [{ axisMode: 'right', forAccessor: 'foo' }], + } as XYDataLayerConfig, + ], + }; + const component = shallow(); + + expect( + component.find(AxisSettingsPopover).at(0).prop('setCurrentTimeMarkerVisibility') + ).toBeFalsy(); + expect( + component.find(AxisSettingsPopover).at(1).prop('setCurrentTimeMarkerVisibility') + ).toBeTruthy(); + expect(component.find(AxisSettingsPopover).at(1).prop('currentTimeMarkerVisible')).toBe(true); + expect( + component.find(AxisSettingsPopover).at(2).prop('setCurrentTimeMarkerVisibility') + ).toBeFalsy(); + }); + it('should pass in information about current data bounds', () => { const state = testState(); frame.activeData = { diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts index f1f0f2b933dee..fde123afdd8c9 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts @@ -556,6 +556,7 @@ function buildSuggestion({ yTitle: currentState?.yTitle, yRightTitle: currentState?.yRightTitle, hideEndzones: currentState?.hideEndzones, + showCurrentTimeMarker: currentState?.showCurrentTimeMarker, valuesInLegend: currentState?.valuesInLegend, yLeftExtent: currentState?.yLeftExtent, yRightExtent: currentState?.yRightExtent, diff --git a/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx b/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx index 19e5d69b7b32c..09c6cb19dca94 100644 --- a/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx +++ b/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx @@ -12,6 +12,7 @@ import { EuiContextMenuPanel, EuiFlexItem, EuiPopover, + EuiPopoverTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -72,7 +73,7 @@ export const AnomalyContextMenu: FC = ({ > ); @@ -92,10 +93,7 @@ export const AnomalyContextMenu: FC = ({ )} data-test-subj="mlAnomalyAttachChartsToCasesButton" > - + ); } @@ -128,6 +126,11 @@ export const AnomalyContextMenu: FC = ({ panelPaddingSize="none" anchorPosition="downLeft" > + + {i18n.translate('xpack.ml.explorer.anomalies.actionsPopoverLabel', { + defaultMessage: 'Anomaly charts', + })} + diff --git a/x-pack/plugins/ml/public/application/explorer/anomaly_timeline.tsx b/x-pack/plugins/ml/public/application/explorer/anomaly_timeline.tsx index 6c062aa915491..4761d79b612cc 100644 --- a/x-pack/plugins/ml/public/application/explorer/anomaly_timeline.tsx +++ b/x-pack/plugins/ml/public/application/explorer/anomaly_timeline.tsx @@ -213,7 +213,7 @@ export const AnomalyTimeline: FC = React.memo( name: ( ), 'data-test-subj': 'mlAnomalyTimelinePanelAttachToCaseButton', @@ -225,7 +225,7 @@ export const AnomalyTimeline: FC = React.memo( title: ( ), items: [ diff --git a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx index b1aec1e10cfed..5651e197b9617 100644 --- a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx +++ b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx @@ -35,7 +35,7 @@ export function registerAnomalyChartsCasesAttachment( event: ( ), timelineAvatar: PLUGIN_ICON, diff --git a/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx b/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx index 1bac62539e2a0..06c441ac05b61 100644 --- a/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx +++ b/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx @@ -36,7 +36,7 @@ export function registerAnomalySwimLaneCasesAttachment( event: ( ), timelineAvatar: PLUGIN_ICON, diff --git a/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx b/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx index 71180f2c2c92d..7d5ab0be65033 100644 --- a/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx +++ b/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { createContext } from 'react'; +import React, { createContext, useState } from 'react'; import type { TimeRange } from '@kbn/es-query'; import { RefreshInterval } from '@kbn/data-plugin/public'; +import useUnmount from 'react-use/lib/useUnmount'; import { GlobalState } from '../../url_state'; import { MonitoringStartPluginDependencies, MonitoringStartServices } from '../../types'; import { Legacy } from '../../legacy_shims'; @@ -42,9 +43,11 @@ export const GlobalStateProvider: React.FC = ({ children, }) => { const localState: State = {}; - const state = new GlobalState(query, toasts, localState as { [key: string]: unknown }); + const [globalState] = useState( + () => new GlobalState(query, toasts, localState as { [key: string]: unknown }) + ); - const initialState: any = state.getState(); + const initialState: any = globalState.getState(); for (const key in initialState) { if (!initialState.hasOwnProperty(key)) { continue; @@ -55,7 +58,7 @@ export const GlobalStateProvider: React.FC = ({ localState.save = () => { const newState = { ...localState }; delete newState.save; - state.setState(newState); + globalState.setState(newState); }; // default to an active refresh interval if it's not conflicting with user-defined values @@ -65,5 +68,9 @@ export const GlobalStateProvider: React.FC = ({ localState.save(); } + useUnmount(() => { + globalState.destroy(); + }); + return {children}; }; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts index 0cee2108e2ede..a19e7a6133a09 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts @@ -8,9 +8,9 @@ import type { LegacyRequest, MonitoringCore } from '../../../../types'; import type { MonitoringConfig } from '../../../../config'; import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { getNewIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; import { getHealthRequestQueryRT } from '../../../../../common/http_api/_health'; import type { TimeRange } from '../../../../../common/http_api/shared'; -import { INDEX_PATTERN, INDEX_PATTERN_ENTERPRISE_SEARCH } from '../../../../../common/constants'; import { fetchMonitoredClusters } from './monitored_clusters'; import { fetchMetricbeatErrors } from './metricbeat'; @@ -21,13 +21,7 @@ const DEFAULT_QUERY_TIMEOUT_SECONDS = 15; export function registerV1HealthRoute(server: MonitoringCore) { const validateQuery = createValidationFunction(getHealthRequestQueryRT); - - const withCCS = (indexPattern: string) => { - if (server.config.ui.ccs.enabled) { - return `${indexPattern},*:${indexPattern}`; - } - return indexPattern; - }; + const { config } = server; server.route({ method: 'get', @@ -44,7 +38,7 @@ export function registerV1HealthRoute(server: MonitoringCore) { const timeout = req.query.timeout || DEFAULT_QUERY_TIMEOUT_SECONDS; const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); - const settings = extractSettings(server.config); + const settings = extractSettings(config); const fetchArgs: FetchParameters = { timeout, @@ -53,11 +47,19 @@ export function registerV1HealthRoute(server: MonitoringCore) { logger, }; + const monitoringIndex = [ + getNewIndexPatterns({ config, moduleType: 'elasticsearch' }), + getNewIndexPatterns({ config, moduleType: 'kibana' }), + getNewIndexPatterns({ config, moduleType: 'logstash' }), + getNewIndexPatterns({ config, moduleType: 'beats' }), + ].join(','); + const entSearchIndex = getNewIndexPatterns({ config, moduleType: 'enterprisesearch' }); + const monitoredClustersFn = () => fetchMonitoredClusters({ ...fetchArgs, - monitoringIndex: withCCS(INDEX_PATTERN), - entSearchIndex: withCCS(INDEX_PATTERN_ENTERPRISE_SEARCH), + monitoringIndex, + entSearchIndex, }).catch((err: Error) => { logger.error(`_health: failed to retrieve monitored clusters:\n${err.stack}`); return { error: err.message }; @@ -66,7 +68,7 @@ export function registerV1HealthRoute(server: MonitoringCore) { const metricbeatErrorsFn = () => fetchMetricbeatErrors({ ...fetchArgs, - metricbeatIndex: server.config.ui.metricbeat.index, + metricbeatIndex: config.ui.metricbeat.index, }).catch((err: Error) => { logger.error(`_health: failed to retrieve metricbeat data:\n${err.stack}`); return { error: err.message }; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.test.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.test.ts index 9248af4cae823..d8ec5303c8f15 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.test.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.test.ts @@ -34,6 +34,22 @@ describe(__filename, () => { }, }, }, + { + key: 'node_two', + shard: { + by_index: { + buckets: [ + { + key: '.ds-metrics-elasticsearch.shard-default-2022.08.16-000001', + last_seen: { + value: 123, + value_as_string: '2022-08-06', + }, + }, + ], + }, + }, + }, ], }, }, @@ -52,6 +68,14 @@ describe(__filename, () => { }, }, }, + node_two: { + shard: { + package: { + index: '.ds-metrics-elasticsearch.shard-default-2022.08.16-000001', + lastSeen: '2022-08-06', + }, + }, + }, }, }, }); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts index 6a5c0fa8f796a..4b7a9f1c49856 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts @@ -13,6 +13,7 @@ enum CollectionMode { Internal = 'internal-monitoring', Metricbeat7 = 'metricbeat-7', Metricbeat8 = 'metricbeat-8', + Package = 'package', Unknown = 'unknown', } @@ -42,11 +43,13 @@ const metricbeatMonitoring7Pattern = /(.*:)?\.monitoring-(es|kibana|beats|logstash|ent-search)-7.*-mb.*/; const metricbeatMonitoring8Pattern = /(.*:)?\.ds-\.monitoring-(es|kibana|beats|logstash|ent-search)-8-mb.*/; +const packagePattern = /(.*:)?\.ds-metrics-(elasticsearch|kibana|logstash)\..*/; const getCollectionMode = (index: string): CollectionMode => { if (internalMonitoringPattern.test(index)) return CollectionMode.Internal; if (metricbeatMonitoring7Pattern.test(index)) return CollectionMode.Metricbeat7; if (metricbeatMonitoring8Pattern.test(index)) return CollectionMode.Metricbeat8; + if (packagePattern.test(index)) return CollectionMode.Package; return CollectionMode.Unknown; }; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/monitored_clusters_query.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/monitored_clusters_query.ts index 5baf6e11c4082..056039c19a7dc 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/monitored_clusters_query.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/monitored_clusters_query.ts @@ -9,6 +9,12 @@ import { TimeRange } from '../../../../../../common/http_api/shared'; const MAX_BUCKET_SIZE = 100; +const getDataset = (product: string) => (metricset: string) => + `${product}.stack_monitoring.${metricset}`; +const getElasticsearchDataset = getDataset('elasticsearch'); +const getKibanaDataset = getDataset('kibana'); +const getLogstashDataset = getDataset('logstash'); + interface QueryOptions { timeRange?: TimeRange; timeout: number; // in seconds @@ -72,6 +78,11 @@ export const persistentMetricsetsQuery = ({ timeout }: QueryOptions) => { 'metricset.name': 'shard', }, }, + { + term: { + 'data_stream.dataset': getElasticsearchDataset('shard'), + }, + }, ]; const logstashStateMatches = [ @@ -85,6 +96,11 @@ export const persistentMetricsetsQuery = ({ timeout }: QueryOptions) => { 'metricset.name': 'node', }, }, + { + term: { + 'data_stream.dataset': getLogstashDataset('node'), + }, + }, ]; const metricsetsAggregations = { @@ -260,6 +276,11 @@ const clusterAggregation = { 'metricset.name': 'cluster_stats', }, }, + { + term: { + 'data_stream.dataset': getElasticsearchDataset('cluster_stats'), + }, + }, ], }, }, @@ -279,6 +300,11 @@ const clusterAggregation = { 'metricset.name': 'ccr', }, }, + { + term: { + 'data_stream.dataset': getElasticsearchDataset('ccr'), + }, + }, ], }, }, @@ -298,6 +324,11 @@ const clusterAggregation = { 'metricset.name': 'index', }, }, + { + term: { + 'data_stream.dataset': getElasticsearchDataset('index'), + }, + }, ], }, }, @@ -317,6 +348,11 @@ const clusterAggregation = { 'metricset.name': 'index_summary', }, }, + { + term: { + 'data_stream.dataset': getElasticsearchDataset('index_summary'), + }, + }, ], }, }, @@ -336,6 +372,11 @@ const clusterAggregation = { 'metricset.name': 'index_recovery', }, }, + { + term: { + 'data_stream.dataset': getElasticsearchDataset('index_recovery'), + }, + }, ], }, }, @@ -364,6 +405,11 @@ const esAggregation = { 'metricset.name': 'node_stats', }, }, + { + term: { + 'data_stream.dataset': getElasticsearchDataset('node_stats'), + }, + }, ], }, }, @@ -391,6 +437,11 @@ const kibanaAggregation = { 'metricset.name': 'stats', }, }, + { + term: { + 'data_stream.dataset': getKibanaDataset('stats'), + }, + }, ], }, }, @@ -418,6 +469,11 @@ const logstashAggregation = { 'metricset.name': 'node_stats', }, }, + { + term: { + 'data_stream.dataset': getLogstashDataset('node_stats'), + }, + }, ], }, }, diff --git a/x-pack/plugins/observability/public/application/application.test.tsx b/x-pack/plugins/observability/public/application/application.test.tsx index d630988446aa6..41c97f41a0bc2 100644 --- a/x-pack/plugins/observability/public/application/application.test.tsx +++ b/x-pack/plugins/observability/public/application/application.test.tsx @@ -12,7 +12,7 @@ import { Observable } from 'rxjs'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { themeServiceMock } from '@kbn/core/public/mocks'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import { ObservabilityPublicPluginsStart } from '../plugin'; +import { ConfigSchema, ObservabilityPublicPluginsStart } from '../plugin'; import { createObservabilityRuleTypeRegistryMock } from '../rules/observability_rule_type_registry_mock'; import { renderApp } from '.'; @@ -66,9 +66,16 @@ describe('renderApp', () => { theme$: themeServiceMock.createTheme$(), } as unknown as AppMountParameters; + const config = { + unsafe: { + alertDetails: { enabled: false }, + }, + } as ConfigSchema; + expect(() => { const unmount = renderApp({ core, + config, plugins, appMountParameters: params, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index 16738c81aa540..2398387a24e08 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -23,7 +23,7 @@ import { DatePickerContextProvider } from '../context/date_picker_context'; import { HasDataContextProvider } from '../context/has_data_context'; import { PluginContext } from '../context/plugin_context'; import { useRouteParams } from '../hooks/use_route_params'; -import { ObservabilityPublicPluginsStart } from '../plugin'; +import { ConfigSchema, ObservabilityPublicPluginsStart } from '../plugin'; import { routes } from '../routes'; import { ObservabilityRuleTypeRegistry } from '../rules/create_observability_rule_type_registry'; @@ -47,6 +47,7 @@ function App() { export const renderApp = ({ core, + config, plugins, appMountParameters, observabilityRuleTypeRegistry, @@ -55,6 +56,7 @@ export const renderApp = ({ isDev, }: { core: CoreStart; + config: ConfigSchema; plugins: ObservabilityPublicPluginsStart; observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; appMountParameters: AppMountParameters; @@ -86,6 +88,7 @@ export const renderApp = ({ > { from: '2020-10-08T06:00:00.000Z', to: '2020-10-08T07:00:00.000Z', }); + const config = { + unsafe: { + alertDetails: { enabled: false }, + }, + } as ConfigSchema; jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ appMountParameters: {} as AppMountParameters, core: {} as CoreStart, + config, plugins: {} as ObservabilityPublicPluginsStart, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), ObservabilityPageTemplate: KibanaPageTemplate, diff --git a/x-pack/plugins/observability/public/context/plugin_context.tsx b/x-pack/plugins/observability/public/context/plugin_context.tsx index b4e6889d957b2..2912fe967cab0 100644 --- a/x-pack/plugins/observability/public/context/plugin_context.tsx +++ b/x-pack/plugins/observability/public/context/plugin_context.tsx @@ -9,8 +9,10 @@ import { AppMountParameters } from '@kbn/core/public'; import { createContext } from 'react'; import { ObservabilityRuleTypeRegistry } from '../rules/create_observability_rule_type_registry'; import type { LazyObservabilityPageTemplateProps } from '../components/shared/page_template/lazy_page_template'; +import { ConfigSchema } from '../plugin'; export interface PluginContextValue { + config: ConfigSchema; appMountParameters: AppMountParameters; observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; ObservabilityPageTemplate: React.ComponentType; diff --git a/x-pack/plugins/observability/public/pages/404.tsx b/x-pack/plugins/observability/public/pages/404.tsx new file mode 100644 index 0000000000000..be96cf88bbce4 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/404.tsx @@ -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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiCallOut } from '@elastic/eui'; +import { usePluginContext } from '../hooks/use_plugin_context'; + +function PageNotFound() { + const { ObservabilityPageTemplate } = usePluginContext(); + + return ( + + + } + data-test-subj={'observabilityPageNotFoundBanner'} + > +

+ +

+
+
+ ); +} + +// eslint-disable-next-line import/no-default-export +export default PageNotFound; diff --git a/x-pack/plugins/observability/public/pages/alert_details/index.tsx b/x-pack/plugins/observability/public/pages/alert_details/index.tsx index c8611d4e200a4..2601795d2bb83 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/index.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/index.tsx @@ -13,6 +13,7 @@ import { ObservabilityAppServices } from '../../application/types'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { paths } from '../../config/paths'; +import PageNotFound from '../404'; // import { useParams } from 'react-router'; // import { AlertDetailsPathParams } from './types'; @@ -20,7 +21,7 @@ import { paths } from '../../config/paths'; export function AlertDetailsPage() { const { http } = useKibana().services; - const { ObservabilityPageTemplate } = usePluginContext(); + const { ObservabilityPageTemplate, config } = usePluginContext(); // const { alertId } = useParams(); const alert = {}; @@ -33,6 +34,11 @@ export function AlertDetailsPage() { }, ]); + // Redirect to the the 404 page when the user hit the page url directly in the browser while the feature flag is off. + if (!config.unsafe.alertDetails.enabled) { + return ; + } + return ( diff --git a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.test.tsx index cd5c892fdbb19..85593ebd6b333 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.test.tsx @@ -13,6 +13,10 @@ import { inventoryThresholdAlert } from '../../../rules/fixtures/example_alerts' import { RULE_DETAILS_PAGE_ID } from '../../rule_details/types'; import { createObservabilityRuleTypeRegistryMock } from '../../../rules/observability_rule_type_registry_mock'; import { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; +import * as pluginContext from '../../../hooks/use_plugin_context'; +import { ConfigSchema, ObservabilityPublicPluginsStart } from '../../../plugin'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; const mockUseKibanaReturnValue = kibanaStartMock.startContract(); @@ -25,6 +29,21 @@ jest.mock('../../../hooks/use_get_user_cases_permissions', () => ({ useGetUserCasesPermissions: jest.fn(() => ({})), })); +const config = { + unsafe: { + alertDetails: { enabled: false }, + }, +} as ConfigSchema; + +jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ + appMountParameters: {} as AppMountParameters, + core: {} as CoreStart, + config, + plugins: {} as ObservabilityPublicPluginsStart, + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), + ObservabilityPageTemplate: KibanaPageTemplate, +})); + describe('ObservabilityActions component', () => { const setup = async (pageId: string) => { const props: ObservabilityActionsProps = { @@ -53,14 +72,14 @@ describe('ObservabilityActions component', () => { const wrapper = await setup(RULE_DETAILS_PAGE_ID); wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click'); expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(0); - expect(wrapper.find('[data-test-subj~="viewAlertDetails"]').hostNodes().length).toBe(1); + expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').hostNodes().length).toBe(1); }); it('should show "View rule details" menu item', async () => { const wrapper = await setup('nothing'); wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click'); expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1); - expect(wrapper.find('[data-test-subj~="viewAlertDetails"]').hostNodes().length).toBe(1); + expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').hostNodes().length).toBe(1); }); it('should create a valid link for rule details page', async () => { diff --git a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx index 4d64c1e93d031..eddead1fa90de 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx @@ -19,6 +19,7 @@ import React, { useMemo, useState, useCallback } from 'react'; import { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; import { CommentType } from '@kbn/cases-plugin/common'; import type { ActionProps } from '@kbn/timelines-plugin/common'; +import { usePluginContext } from '../../../hooks/use_plugin_context'; import { useKibana } from '../../../utils/kibana_react'; import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions'; import { parseAlert } from './parse_alert'; @@ -53,6 +54,7 @@ export function ObservabilityActions({ const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {}); const [openActionsPopoverId, setActionsPopover] = useState(null); const { cases, http } = useKibana().services; + const { config } = usePluginContext(); const parseObservabilityAlert = useMemo( () => parseAlert(observabilityRuleTypeRegistry), @@ -141,29 +143,27 @@ export function ObservabilityActions({ : []), ...[ - { - closeActionsPopover(); - setFlyoutAlert(alert); - }} - > - {translations.alertsTable.viewAlertDetailsButtonText} - , + config.unsafe.alertDetails.enabled && linkToAlert ? ( + + {translations.alertsTable.viewAlertDetailsButtonText} + + ) : ( + { + closeActionsPopover(); + setFlyoutAlert(alert); + }} + > + {translations.alertsTable.viewAlertDetailsButtonText} + + ), ], - - ...(linkToAlert - ? [ - - {translations.alertsTable.viewAlertDetailsPageButtonText} - , - ] - : []), ]; }, [ userCasesPermissions.create, @@ -171,10 +171,11 @@ export function ObservabilityActions({ handleAddToExistingCaseClick, handleAddToNewCaseClick, linkToRule, - alert, + config.unsafe.alertDetails.enabled, linkToAlert, - setFlyoutAlert, closeActionsPopover, + setFlyoutAlert, + alert, ]); const actionsToolTip = diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index 17d67d15f4287..2bf4c6bd04d4c 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -220,7 +220,7 @@ function AlertsPage() { {i18n.translate('xpack.observability.alertsTitle', { defaultMessage: 'Alerts' })} diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index 2e384e83fd03d..60d17cf9e6beb 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -25,6 +25,7 @@ import { newsFeedFetchData } from './mock/news_feed.mock'; import { emptyResponse as emptyUptimeResponse, fetchUptimeData } from './mock/uptime.mock'; import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock'; import { ApmIndicesConfig } from '../../../common/typings'; +import { ConfigSchema } from '../../plugin'; function unregisterAll() { unregisterDataHandler({ appName: 'apm' }); @@ -74,6 +75,12 @@ const withCore = makeDecorator({ }, } as unknown as Partial); + const config = { + unsafe: { + alertDetails: { enabled: false }, + }, + } as ConfigSchema; + return ( @@ -82,6 +89,7 @@ const withCore = makeDecorator({ appMountParameters: { setHeaderActionMenu: () => {}, } as unknown as AppMountParameters, + config, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), ObservabilityPageTemplate: KibanaPageTemplate, }} diff --git a/x-pack/plugins/observability/public/pages/rules/index.test.tsx b/x-pack/plugins/observability/public/pages/rules/index.test.tsx index cc8a093e32333..5c76a3d988078 100644 --- a/x-pack/plugins/observability/public/pages/rules/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/rules/index.test.tsx @@ -10,7 +10,7 @@ import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; import { CoreStart } from '@kbn/core/public'; -import { ObservabilityPublicPluginsStart } from '../../plugin'; +import { ConfigSchema, ObservabilityPublicPluginsStart } from '../../plugin'; import { RulesPage } from '.'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import * as pluginContext from '../../hooks/use_plugin_context'; @@ -34,8 +34,15 @@ jest.mock('@kbn/triggers-actions-ui-plugin/public', () => ({ useLoadRuleTypes: jest.fn(), })); +const config = { + unsafe: { + alertDetails: { enabled: false }, + }, +} as ConfigSchema; + jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ appMountParameters: {} as AppMountParameters, + config, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), ObservabilityPageTemplate: KibanaPageTemplate, kibanaFeatures: [], diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 97b3746671c9d..6c84eb2588a0d 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -52,6 +52,11 @@ import { createExploratoryViewUrl } from './components/shared/exploratory_view/c import { createUseRulesLink } from './hooks/create_use_rules_link'; import getAppDataView from './utils/observability_data_views/get_app_data_view'; +export interface ConfigSchema { + unsafe: { + alertDetails: { enabled: boolean }; + }; +} export type ObservabilityPublicSetup = ReturnType; export interface ObservabilityPublicPluginsSetup { @@ -133,7 +138,7 @@ export class Plugin }), ]; - constructor(private readonly initContext: PluginInitializerContext) {} + constructor(private readonly initContext: PluginInitializerContext) {} public setup( coreSetup: CoreSetup, @@ -141,6 +146,7 @@ export class Plugin ) { const category = DEFAULT_APP_CATEGORIES.observability; const euiIconType = 'logoObservability'; + const config = this.initContext.config.get(); createCallObservabilityApi(coreSetup.http); @@ -157,6 +163,7 @@ export class Plugin const { ruleTypeRegistry, actionTypeRegistry } = pluginsStart.triggersActionsUi; return renderApp({ core: coreStart, + config, plugins: { ...pluginsStart, ruleTypeRegistry, actionTypeRegistry }, appMountParameters: params, observabilityRuleTypeRegistry: this.observabilityRuleTypeRegistry, diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx index ef197f7dfc8d5..f44bf8fb1e529 100644 --- a/x-pack/plugins/observability/public/utils/test_helper.tsx +++ b/x-pack/plugins/observability/public/utils/test_helper.tsx @@ -18,6 +18,7 @@ import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { PluginContext } from '../context/plugin_context'; import { createObservabilityRuleTypeRegistryMock } from '../rules/observability_rule_type_registry_mock'; +import { ConfigSchema } from '../plugin'; const appMountParameters = { setHeaderActionMenu: () => {} } as unknown as AppMountParameters; @@ -26,6 +27,12 @@ export const data = dataPluginMock.createStartContract(); const observabilityRuleTypeRegistry = createObservabilityRuleTypeRegistryMock(); +const config = { + unsafe: { + alertDetails: { enabled: false }, + }, +} as ConfigSchema; + export const render = (component: React.ReactNode) => { return testLibRender( @@ -33,6 +40,7 @@ export const render = (component: React.ReactNode) => { ({ + name, + template: { + mappings: { + properties: { + '@timestamp': { + type: 'date', + format: 'date_optional_time||epoch_millis', + }, + slo: { + properties: { + id: { + type: 'keyword', + ignore_above: 256, + }, + numerator: { + type: 'long', + }, + denominator: { + type: 'long', + }, + context: { + type: 'flattened', + }, + }, + }, + }, + }, + }, + _meta: { + description: 'Mappings for SLO rollup data', + version: 1, + }, +}); diff --git a/x-pack/plugins/observability/server/assets/component_templates/slo_settings_template.ts b/x-pack/plugins/observability/server/assets/component_templates/slo_settings_template.ts new file mode 100644 index 0000000000000..1cec9c2c03900 --- /dev/null +++ b/x-pack/plugins/observability/server/assets/component_templates/slo_settings_template.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 getSLOSettingsTemplate = (name: string) => ({ + name, + template: { + settings: {}, + }, + _meta: { + description: 'Settings for SLO rollup data', + version: 1, + }, +}); diff --git a/x-pack/plugins/observability/server/assets/constants.ts b/x-pack/plugins/observability/server/assets/constants.ts new file mode 100644 index 0000000000000..09d22022caffd --- /dev/null +++ b/x-pack/plugins/observability/server/assets/constants.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. + */ + +export const SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME = 'observability-slo-mappings'; +export const SLO_COMPONENT_TEMPLATE_SETTINGS_NAME = 'observability-slo-settings'; +export const SLO_INDEX_TEMPLATE_NAME = 'observability-slo-data'; +export const SLO_INGEST_PIPELINE_NAME = 'observability-slo-monthly-index'; +export const SLO_RESOURCES_VERSION = 1; diff --git a/x-pack/plugins/observability/server/assets/index_templates/slo_index_templates.ts b/x-pack/plugins/observability/server/assets/index_templates/slo_index_templates.ts new file mode 100644 index 0000000000000..519b726faf4c1 --- /dev/null +++ b/x-pack/plugins/observability/server/assets/index_templates/slo_index_templates.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 getSLOIndexTemplate = (name: string, indexPattern: string, composedOf: string[]) => ({ + name, + index_patterns: [indexPattern], + composed_of: composedOf, + priority: 500, + _meta: { + description: 'Template for SLO rollup data', + version: 1, + }, +}); diff --git a/x-pack/plugins/observability/server/assets/ingest_templates/slo_pipeline_template.ts b/x-pack/plugins/observability/server/assets/ingest_templates/slo_pipeline_template.ts new file mode 100644 index 0000000000000..4aaefc6ccc178 --- /dev/null +++ b/x-pack/plugins/observability/server/assets/ingest_templates/slo_pipeline_template.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. + */ + +export const getSLOPipelineTemplate = (id: string, indexNamePrefix: string) => ({ + id, + description: 'Monthly date-time index naming for SLO data', + processors: [ + { + date_index_name: { + field: '@timestamp', + index_name_prefix: indexNamePrefix, + date_rounding: 'M', + }, + }, + ], + _meta: { + description: 'SLO ingest pipeline', + version: 1, + }, +}); diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index 1fd3e346416cf..c8c249426bca5 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -31,12 +31,15 @@ const configSchema = schema.object({ slo: schema.object({ enabled: schema.boolean({ defaultValue: false }), }), + alertDetails: schema.object({ + enabled: schema.boolean({ defaultValue: false }), + }), }), }); export const config: PluginConfigDescriptor = { exposeToBrowser: { - unsafe: false, + unsafe: true, }, schema: configSchema, }; diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index c907f8513b636..5b47bbead8300 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -15,6 +15,7 @@ import { import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; import { PluginSetupContract as FeaturesSetup } from '@kbn/features-plugin/server'; import { createUICapabilities } from '@kbn/cases-plugin/common'; +import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; import { ObservabilityConfig } from '.'; import { bootstrapAnnotations, @@ -25,12 +26,14 @@ import { uiSettings } from './ui_settings'; import { registerRoutes } from './routes/register_routes'; import { getGlobalObservabilityServerRouteRepository } from './routes/get_global_observability_server_route_repository'; import { casesFeatureId, observabilityFeatureId } from '../common'; +import { slo } from './saved_objects'; export type ObservabilityPluginSetup = ReturnType; interface PluginSetup { features: FeaturesSetup; ruleRegistry: RuleRegistryPluginSetupContract; + spaces: SpacesPluginStart; } export class ObservabilityPlugin implements Plugin { @@ -135,8 +138,14 @@ export class ObservabilityPlugin implements Plugin { }); } + if (config.unsafe.slo.enabled) { + core.savedObjects.registerType(slo); + } + const start = () => core.getStartServices().then(([coreStart]) => coreStart); + const { spacesService } = plugins.spaces; + const { ruleDataService } = plugins.ruleRegistry; registerRoutes({ @@ -147,6 +156,7 @@ export class ObservabilityPlugin implements Plugin { logger: this.initContext.logger.get(), repository: getGlobalObservabilityServerRouteRepository(config), ruleDataService, + spacesService, }); return { diff --git a/x-pack/plugins/observability/server/routes/register_routes.ts b/x-pack/plugins/observability/server/routes/register_routes.ts index a352fb0024d3a..654c50042148c 100644 --- a/x-pack/plugins/observability/server/routes/register_routes.ts +++ b/x-pack/plugins/observability/server/routes/register_routes.ts @@ -14,6 +14,7 @@ import { CoreSetup, CoreStart, Logger, RouteRegistrar } from '@kbn/core/server'; import Boom from '@hapi/boom'; import { errors } from '@elastic/elasticsearch'; import { RuleDataPluginService } from '@kbn/rule-registry-plugin/server'; +import { SpacesServiceStart } from '@kbn/spaces-plugin/server'; import { ObservabilityRequestHandlerContext } from '../types'; import { AbstractObservabilityServerRouteRepository } from './types'; @@ -22,6 +23,7 @@ export function registerRoutes({ core, logger, ruleDataService, + spacesService, }: { core: { setup: CoreSetup; @@ -30,6 +32,7 @@ export function registerRoutes({ repository: AbstractObservabilityServerRouteRepository; logger: Logger; ruleDataService: RuleDataPluginService; + spacesService: SpacesServiceStart; }) { const routes = Object.values(repository); @@ -63,6 +66,7 @@ export function registerRoutes({ logger, params: decodedParams, ruleDataService, + spacesService, })) as any; return response.ok({ body: data }); diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index 687f84023e799..e868bc99a5417 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createSloParamsSchema } from '../../schema'; +import { createSLOParamsSchema } from '../../types/schema'; import { createObservabilityServerRoute } from '../create_observability_server_route'; const createSLORoute = createObservabilityServerRoute({ @@ -13,7 +13,7 @@ const createSLORoute = createObservabilityServerRoute({ options: { tags: [], }, - params: createSloParamsSchema, + params: createSLOParamsSchema, handler: async ({ context, request, params }) => { return { success: true }; }, diff --git a/x-pack/plugins/observability/server/routes/types.ts b/x-pack/plugins/observability/server/routes/types.ts index 768b94b0b7335..b64e3e37bd8f3 100644 --- a/x-pack/plugins/observability/server/routes/types.ts +++ b/x-pack/plugins/observability/server/routes/types.ts @@ -8,6 +8,7 @@ import type { EndpointOf, ReturnOf, ServerRouteRepository } from '@kbn/server-ro import { CoreSetup, CoreStart, KibanaRequest, Logger } from '@kbn/core/server'; import { RuleDataPluginService } from '@kbn/rule-registry-plugin/server'; +import { SpacesServiceStart } from '@kbn/spaces-plugin/server'; import { ObservabilityServerRouteRepository } from './get_global_observability_server_route_repository'; import { ObservabilityRequestHandlerContext } from '../types'; @@ -19,6 +20,7 @@ export interface ObservabilityRouteHandlerResources { setup: CoreSetup; }; ruleDataService: RuleDataPluginService; + spacesService: SpacesServiceStart; request: KibanaRequest; context: ObservabilityRequestHandlerContext; logger: Logger; diff --git a/x-pack/plugins/observability/server/saved_objects/index.ts b/x-pack/plugins/observability/server/saved_objects/index.ts new file mode 100644 index 0000000000000..6e4c8b66f7521 --- /dev/null +++ b/x-pack/plugins/observability/server/saved_objects/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 { slo, SO_SLO_TYPE } from './slo'; diff --git a/x-pack/plugins/observability/server/saved_objects/slo.ts b/x-pack/plugins/observability/server/saved_objects/slo.ts new file mode 100644 index 0000000000000..13b418e5442c0 --- /dev/null +++ b/x-pack/plugins/observability/server/saved_objects/slo.ts @@ -0,0 +1,58 @@ +/* + * Copyright 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 { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { SavedObject } from '@kbn/core/server'; + +import { StoredSLO } from '../types/models'; + +export const SO_SLO_TYPE = 'slo'; + +export const slo: SavedObjectsType = { + name: SO_SLO_TYPE, + hidden: false, + namespaceType: 'multiple-isolated', + mappings: { + dynamic: false, + properties: { + name: { type: 'text' }, + description: { type: 'text' }, + indicator: { + properties: { + type: { type: 'keyword' }, + params: { type: 'flattened' }, + }, + }, + time_window: { + properties: { + duration: { type: 'keyword' }, + is_rolling: { type: 'boolean' }, + }, + }, + budgeting_method: { type: 'keyword' }, + objective: { + properties: { + target: { type: 'float' }, + }, + }, + settings: { + properties: { + destination_index: { type: 'text' }, + }, + }, + created_at: { type: 'date' }, + updated_at: { type: 'date' }, + }, + }, + management: { + displayName: 'SLO', + importableAndExportable: true, + getTitle(sloSavedObject: SavedObject) { + return `SLO: [${sloSavedObject.attributes.name}]`; + }, + }, +}; diff --git a/x-pack/plugins/observability/server/services/slo/index.ts b/x-pack/plugins/observability/server/services/slo/index.ts new file mode 100644 index 0000000000000..39c288bbbf539 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './resource_installer'; +export * from './slo_repository'; diff --git a/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts b/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts new file mode 100644 index 0000000000000..000c991e42ecb --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { loggerMock } from '@kbn/logging-mocks'; +import { + SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME, + SLO_COMPONENT_TEMPLATE_SETTINGS_NAME, + SLO_INDEX_TEMPLATE_NAME, + SLO_INGEST_PIPELINE_NAME, + SLO_RESOURCES_VERSION, +} from '../../assets/constants'; +import { ResourceInstaller } from './resource_installer'; + +describe('resourceInstaller', () => { + describe("when the common resources don't exist", () => { + it('installs the common resources', async () => { + const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); + mockClusterClient.indices.existsIndexTemplate.mockResponseOnce(false); + const installer = new ResourceInstaller(mockClusterClient, loggerMock.create()); + + await installer.ensureCommonResourcesInstalled(); + + expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ name: SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME }) + ); + expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ name: SLO_COMPONENT_TEMPLATE_SETTINGS_NAME }) + ); + expect(mockClusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( + expect.objectContaining({ name: SLO_INDEX_TEMPLATE_NAME }) + ); + expect(mockClusterClient.ingest.putPipeline).toHaveBeenCalledWith( + expect.objectContaining({ id: SLO_INGEST_PIPELINE_NAME }) + ); + }); + }); + + describe('when the common resources exist', () => { + it('does not install the common resources', async () => { + const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); + mockClusterClient.indices.existsIndexTemplate.mockResponseOnce(true); + mockClusterClient.ingest.getPipeline.mockResponseOnce({ + [SLO_INGEST_PIPELINE_NAME]: { _meta: { version: SLO_RESOURCES_VERSION } }, + } as IngestGetPipelineResponse); + const installer = new ResourceInstaller(mockClusterClient, loggerMock.create()); + + await installer.ensureCommonResourcesInstalled(); + + expect(mockClusterClient.cluster.putComponentTemplate).not.toHaveBeenCalled(); + expect(mockClusterClient.indices.putIndexTemplate).not.toHaveBeenCalled(); + expect(mockClusterClient.ingest.putPipeline).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/observability/server/services/slo/resource_installer.ts b/x-pack/plugins/observability/server/services/slo/resource_installer.ts new file mode 100644 index 0000000000000..92ea496e256df --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/resource_installer.ts @@ -0,0 +1,108 @@ +/* + * Copyright 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 { + ClusterPutComponentTemplateRequest, + IndicesPutIndexTemplateRequest, + IngestPutPipelineRequest, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; + +import { + SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME, + SLO_COMPONENT_TEMPLATE_SETTINGS_NAME, + SLO_INDEX_TEMPLATE_NAME, + SLO_INGEST_PIPELINE_NAME, + SLO_RESOURCES_VERSION, +} from '../../assets/constants'; +import { getSLOMappingsTemplate } from '../../assets/component_templates/slo_mappings_template'; +import { getSLOSettingsTemplate } from '../../assets/component_templates/slo_settings_template'; +import { getSLOIndexTemplate } from '../../assets/index_templates/slo_index_templates'; +import { getSLOPipelineTemplate } from '../../assets/ingest_templates/slo_pipeline_template'; + +export class ResourceInstaller { + constructor(private esClient: ElasticsearchClient, private logger: Logger) {} + + public async ensureCommonResourcesInstalled(spaceId: string = 'default'): Promise { + const alreadyInstalled = await this.areResourcesAlreadyInstalled(); + + if (alreadyInstalled) { + this.logger.debug( + `Skipping installation of resources shared for SLO since they already exist` + ); + return; + } + + try { + await Promise.all([ + this.createOrUpdateComponentTemplate( + getSLOMappingsTemplate(SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME) + ), + this.createOrUpdateComponentTemplate( + getSLOSettingsTemplate(SLO_COMPONENT_TEMPLATE_SETTINGS_NAME) + ), + ]); + + await this.createOrUpdateIndexTemplate( + getSLOIndexTemplate(SLO_INDEX_TEMPLATE_NAME, `${SLO_INDEX_TEMPLATE_NAME}-*`, [ + SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME, + SLO_COMPONENT_TEMPLATE_SETTINGS_NAME, + ]) + ); + + await this.createOrUpdateIngestPipelineTemplate( + getSLOPipelineTemplate( + SLO_INGEST_PIPELINE_NAME, + this.getPipelinePrefix(SLO_RESOURCES_VERSION, spaceId) + ) + ); + } catch (err) { + this.logger.error(`Error installing resources shared for SLO - ${err.message}`); + throw err; + } + } + + private getPipelinePrefix(version: number, spaceId: string): string { + return `${SLO_INDEX_TEMPLATE_NAME}-version-${version}-${spaceId}-`; + } + + private async areResourcesAlreadyInstalled(): Promise { + const indexTemplateExists = await this.esClient.indices.existsIndexTemplate({ + name: SLO_INDEX_TEMPLATE_NAME, + }); + + let ingestPipelineExists = false; + try { + const pipeline = await this.esClient.ingest.getPipeline({ + id: SLO_INGEST_PIPELINE_NAME, + }); + + ingestPipelineExists = + // @ts-ignore _meta is not defined on the type + pipeline && pipeline[SLO_INGEST_PIPELINE_NAME]._meta.version === SLO_RESOURCES_VERSION; + } catch (err) { + return false; + } + + return indexTemplateExists && ingestPipelineExists; + } + + private async createOrUpdateComponentTemplate(template: ClusterPutComponentTemplateRequest) { + this.logger.debug(`Installing SLO component template ${template.name}`); + return this.esClient.cluster.putComponentTemplate(template); + } + + private async createOrUpdateIndexTemplate(template: IndicesPutIndexTemplateRequest) { + this.logger.debug(`Installing SLO index template ${template.name}`); + return this.esClient.indices.putIndexTemplate(template); + } + + private async createOrUpdateIngestPipelineTemplate(template: IngestPutPipelineRequest) { + this.logger.debug(`Installing SLO ingest pipeline template ${template.id}`); + await this.esClient.ingest.putPipeline(template); + } +} diff --git a/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts new file mode 100644 index 0000000000000..8e7b7bbcac427 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright 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 uuid from 'uuid'; +import { SavedObject } from '@kbn/core-saved-objects-common'; +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; + +import { SLO, StoredSLO } from '../../types/models'; +import { SO_SLO_TYPE } from '../../saved_objects'; +import { KibanaSavedObjectsSLORepository } from './slo_repository'; + +const anSLO: SLO = { + id: uuid.v1(), + name: 'irrelevant', + description: 'irrelevant', + indicator: { + type: 'slo.apm.transaction_duration', + params: { + environment: 'irrelevant', + service: 'irrelevant', + transaction_type: 'irrelevant', + transaction_name: 'irrelevant', + 'threshold.us': 200000, + }, + }, + time_window: { + duration: '7d', + is_rolling: true, + }, + budgeting_method: 'occurrences', + objective: { + target: 0.999, + }, + settings: { + destination_index: 'some-index', + }, +}; + +function aStoredSLO(slo: SLO): SavedObject { + return { + id: slo.id, + attributes: { + ...slo, + updated_at: new Date().toISOString(), + created_at: new Date().toISOString(), + }, + type: SO_SLO_TYPE, + references: [], + }; +} + +describe('sloRepository', () => { + let soClientMock: jest.Mocked; + + beforeEach(() => { + soClientMock = savedObjectsClientMock.create(); + }); + + it('saves the SLO', async () => { + soClientMock.create.mockResolvedValueOnce(aStoredSLO(anSLO)); + const repository = new KibanaSavedObjectsSLORepository(soClientMock); + + const savedSLO = await repository.save(anSLO); + + expect(savedSLO).toEqual(anSLO); + expect(soClientMock.create).toHaveBeenCalledWith( + SO_SLO_TYPE, + expect.objectContaining({ + ...anSLO, + updated_at: expect.anything(), + created_at: expect.anything(), + }) + ); + }); + + it('finds an existing SLO', async () => { + const repository = new KibanaSavedObjectsSLORepository(soClientMock); + soClientMock.get.mockResolvedValueOnce(aStoredSLO(anSLO)); + + const foundSLO = await repository.findById(anSLO.id); + + expect(foundSLO).toEqual(anSLO); + expect(soClientMock.get).toHaveBeenCalledWith(SO_SLO_TYPE, anSLO.id); + }); +}); diff --git a/x-pack/plugins/observability/server/services/slo/slo_repository.ts b/x-pack/plugins/observability/server/services/slo/slo_repository.ts new file mode 100644 index 0000000000000..1fa45cba34dc3 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/slo_repository.ts @@ -0,0 +1,49 @@ +/* + * Copyright 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 { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; + +import { StoredSLO, SLO } from '../../types/models'; +import { SO_SLO_TYPE } from '../../saved_objects'; + +export interface SLORepository { + save(slo: SLO): Promise; + findById(id: string): Promise; +} + +export class KibanaSavedObjectsSLORepository implements SLORepository { + constructor(private soClient: SavedObjectsClientContract) {} + + async save(slo: SLO): Promise { + const now = new Date().toISOString(); + const savedSLO = await this.soClient.create(SO_SLO_TYPE, { + ...slo, + created_at: now, + updated_at: now, + }); + + return toSLOModel(savedSLO.attributes); + } + + async findById(id: string): Promise { + const slo = await this.soClient.get(SO_SLO_TYPE, id); + return toSLOModel(slo.attributes); + } +} + +function toSLOModel(slo: StoredSLO): SLO { + return { + id: slo.id, + name: slo.name, + description: slo.description, + indicator: slo.indicator, + time_window: slo.time_window, + budgeting_method: slo.budgeting_method, + objective: slo.objective, + settings: slo.settings, + }; +} diff --git a/x-pack/plugins/observability/server/schema/index.ts b/x-pack/plugins/observability/server/types/models/index.ts similarity index 100% rename from x-pack/plugins/observability/server/schema/index.ts rename to x-pack/plugins/observability/server/types/models/index.ts diff --git a/x-pack/plugins/observability/server/types/models/slo.ts b/x-pack/plugins/observability/server/types/models/slo.ts new file mode 100644 index 0000000000000..94017b50eb65a --- /dev/null +++ b/x-pack/plugins/observability/server/types/models/slo.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { indicatorSchema, rollingTimeWindowSchema } from '../schema'; + +const baseSLOSchema = t.type({ + id: t.string, + name: t.string, + description: t.string, + indicator: indicatorSchema, + time_window: rollingTimeWindowSchema, + budgeting_method: t.literal('occurrences'), + objective: t.type({ + target: t.number, + }), + settings: t.partial({ + destination_index: t.string, + }), +}); + +const storedSLOSchema = t.intersection([ + baseSLOSchema, + t.type({ created_at: t.string, updated_at: t.string }), +]); + +export type SLO = t.TypeOf; +export type StoredSLO = t.TypeOf; diff --git a/x-pack/plugins/observability/server/types/schema/index.ts b/x-pack/plugins/observability/server/types/schema/index.ts new file mode 100644 index 0000000000000..78f557bdcbc7d --- /dev/null +++ b/x-pack/plugins/observability/server/types/schema/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 * from './slo'; diff --git a/x-pack/plugins/observability/server/schema/slo.ts b/x-pack/plugins/observability/server/types/schema/slo.ts similarity index 73% rename from x-pack/plugins/observability/server/schema/slo.ts rename to x-pack/plugins/observability/server/types/schema/slo.ts index 8c248be4db9bb..62495ff26d4f4 100644 --- a/x-pack/plugins/observability/server/schema/slo.ts +++ b/x-pack/plugins/observability/server/types/schema/slo.ts @@ -37,23 +37,36 @@ const apmTransactionErrorRateIndicatorSchema = t.type({ ]), }); -const rollingTimeWindowSchema = t.type({ +export const rollingTimeWindowSchema = t.type({ duration: t.string, is_rolling: t.literal(true), }); -export const createSloParamsSchema = t.type({ - body: t.type({ +export const indicatorSchema = t.union([ + apmTransactionDurationIndicatorSchema, + apmTransactionErrorRateIndicatorSchema, +]); + +const sloOptionalSettingsSchema = t.partial({ + settings: t.partial({ + destination_index: t.string, + }), +}); + +const createSLOBodySchema = t.intersection([ + t.type({ name: t.string, description: t.string, - indicator: t.union([ - apmTransactionDurationIndicatorSchema, - apmTransactionErrorRateIndicatorSchema, - ]), + indicator: indicatorSchema, time_window: rollingTimeWindowSchema, budgeting_method: t.literal('occurrences'), objective: t.type({ target: t.number, }), }), + sloOptionalSettingsSchema, +]); + +export const createSLOParamsSchema = t.type({ + body: createSLOBodySchema, }); diff --git a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts index c332e3dae5087..b04dff5d39ab3 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts @@ -105,7 +105,7 @@ describe('ALL - Live Query', () => { selectAllAgents(); cy.react('SavedQueriesDropdown').type('NOMAPPING{downArrow}{enter}'); // cy.getReact('SavedQueriesDropdown').getCurrentState().should('have.length', 1); // TODO do we need it? - inputQuery('{selectall}{backspace}{selectall}{backspace}select * from users'); + inputQuery('{selectall}{backspace}select * from users;'); cy.wait(1000); submitQuery(); checkResults(); @@ -115,15 +115,14 @@ describe('ALL - Live Query', () => { .should('be.visible') .click(); - cy.react('ReactAce', { props: { value: 'select * from users' } }).should('exist'); + cy.react('ReactAce', { props: { value: 'select * from users;' } }).should('exist'); }); it('should run live pack', () => { cy.contains('New live query').click(); cy.contains('Run a set of queries in a pack.').click(); cy.get(LIVE_QUERY_EDITOR).should('not.exist'); - cy.getBySel('select-live-pack').click(); - cy.contains('Example').click(); + cy.getBySel('select-live-pack').click().type('Example{downArrow}{enter}'); cy.contains('This table contains 3 rows.'); cy.contains('system_memory_linux_elastic'); cy.contains('system_info_elastic'); diff --git a/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts index 64620cf3cc08c..b53651afa7157 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts @@ -19,6 +19,7 @@ import { addIntegration, closeModalIfVisible } from '../../tasks/integrations'; import { DEFAULT_POLICY } from '../../screens/fleet'; import { getSavedQueriesDropdown } from '../../screens/live_query'; import { ROLES } from '../../test'; +import { getRandomInt } from '../../tasks/helpers'; describe('ALL - Packs', () => { const integration = 'Osquery Manager'; @@ -251,7 +252,8 @@ describe('ALL - Packs', () => { beforeEach(() => { login(); }); - const AGENT_NAME = 'PackTest2'; + const randomNumber = getRandomInt(); + const AGENT_NAME = `PackTest${randomNumber}`; const REMOVING_PACK = 'removing-pack'; it('add integration', () => { cy.visit(FLEET_AGENT_POLICIES); @@ -272,7 +274,7 @@ describe('ALL - Packs', () => { findAndClickButton('Save pack'); cy.getBySel('toastCloseButton').click(); - cy.contains(REMOVING_PACK).click(); + cy.react('ScheduledQueryNameComponent', { props: { name: REMOVING_PACK } }).click(); cy.contains(`${REMOVING_PACK} details`).should('exist'); findAndClickButton('Edit'); cy.react('EuiComboBoxInput', { props: { value: AGENT_NAME } }).should('exist'); diff --git a/x-pack/plugins/osquery/cypress/integration/all/saved_queries.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/saved_queries.spec.ts index 18167506fc75c..e4d76ce9438eb 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/saved_queries.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/saved_queries.spec.ts @@ -8,17 +8,18 @@ import { navigateTo } from '../../tasks/navigation'; import { login } from '../../tasks/login'; -import { getSavedQueriesComplexTest } from '../../tasks/saved_queries'; import { ROLES } from '../../test'; -const SAVED_QUERY_ID = 'Saved-Query-Id'; -const SAVED_QUERY_DESCRIPTION = 'Test saved query description'; - describe('ALL - Saved queries', () => { + // const randomNumber = getRandomInt(); + // const SAVED_QUERY_ID = `Saved-Query-Id-${randomNumber}`; + // const SAVED_QUERY_DESCRIPTION = `Test saved query description ${randomNumber}`; + beforeEach(() => { login(ROLES.soc_manager); navigateTo('/app/osquery'); }); - getSavedQueriesComplexTest(SAVED_QUERY_ID, SAVED_QUERY_DESCRIPTION); + // TODO usnkip after FF + // getSavedQueriesComplexTest(SAVED_QUERY_ID, SAVED_QUERY_DESCRIPTION); }); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts index d236555091b3f..8cd90d200bca7 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts @@ -55,7 +55,8 @@ describe('T1 Analyst - READ + runSavedQueries ', () => { submitQuery(); checkResults(); }); - it('should be able to use saved query in a new query', () => { + // TODO UNSKIP AFTER FF + it.skip('should be able to use saved query in a new query', () => { navigateTo('/app/osquery/live_queries'); cy.waitForReact(1000); cy.contains('New live query').should('not.be.disabled').click(); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts index 63a0eb5d1619d..423977675ed0d 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts @@ -17,12 +17,13 @@ import { typeInOsqueryFieldInput, } from '../../tasks/live_query'; import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver'; -import { getSavedQueriesComplexTest } from '../../tasks/saved_queries'; describe('T2 Analyst - READ + Write Live/Saved + runSavedQueries ', () => { const SAVED_QUERY_ID = 'Saved-Query-Id'; - const NEW_SAVED_QUERY_ID = 'Saved-Query-Id-T2'; - const NEW_SAVED_QUERY_DESCRIPTION = 'Test saved query description T2'; + // const randomNumber = getRandomInt(); + // + // const NEW_SAVED_QUERY_ID = `Saved-Query-Id-${randomNumber}`; + // const NEW_SAVED_QUERY_DESCRIPTION = `Test saved query description ${randomNumber}`; beforeEach(() => { login(ROLES.t2_analyst); navigateTo('/app/osquery'); @@ -35,7 +36,8 @@ describe('T2 Analyst - READ + Write Live/Saved + runSavedQueries ', () => { runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query'); }); - getSavedQueriesComplexTest(NEW_SAVED_QUERY_ID, NEW_SAVED_QUERY_DESCRIPTION); + // TODO unskip after FF + // getSavedQueriesComplexTest(NEW_SAVED_QUERY_ID, NEW_SAVED_QUERY_DESCRIPTION); it('should not be able to add nor edit packs', () => { const PACK_NAME = 'removing-pack'; diff --git a/x-pack/plugins/osquery/cypress/tasks/helpers.ts b/x-pack/plugins/osquery/cypress/tasks/helpers.ts new file mode 100644 index 0000000000000..14818a637333e --- /dev/null +++ b/x-pack/plugins/osquery/cypress/tasks/helpers.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 const getRandomInt = () => Math.floor(Math.random() * 100); diff --git a/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts b/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts index d288584b6a169..4de712bd2f55a 100644 --- a/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts +++ b/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts @@ -39,16 +39,21 @@ export const getSavedQueriesComplexTest = (savedQueryId: string, savedQueryDescr cy.contains('Exit fullscreen').should('exist'); // hidden columns + cy.contains('columns hidden').should('not.exist'); cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.cmdline' } }).click(); cy.contains(/Hide column$/).click(); + cy.react('EuiDataGridHeaderCellWrapper', { + props: { id: 'osquery.cwd' }, + }).click(); + cy.contains(/Hide column$/).click(); cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.disk_bytes_written.number' }, }).click(); cy.contains(/Hide column$/).click(); - cy.contains('2 columns hidden').should('exist'); + cy.contains('columns hidden').should('exist'); // change pagination cy.getBySel('pagination-button-next').click().wait(500).click(); - cy.contains('2 columns hidden').should('exist'); + cy.contains('columns hidden').should('exist'); cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover'); cy.contains(/Enter fullscreen$/).should('not.exist'); @@ -60,10 +65,14 @@ export const getSavedQueriesComplexTest = (savedQueryId: string, savedQueryDescr props: { id: 'osquery.egid' }, }).click(); cy.contains(/Sort A-Z$/).click(); - cy.contains('2 columns hidden').should('exist'); + cy.contains('columns hidden').should('exist'); cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover'); cy.contains(/Enter fullscreen$/).should('exist'); + // visit Status results + cy.react('EuiTab', { props: { id: 'status' } }).click(); + cy.react('EuiTableRow').should('have.lengthOf', 1); + // save new query cy.contains('Exit full screen').should('not.exist'); cy.contains('Save for later').click(); @@ -71,11 +80,7 @@ export const getSavedQueriesComplexTest = (savedQueryId: string, savedQueryDescr findFormFieldByRowsLabelAndType('ID', savedQueryId); findFormFieldByRowsLabelAndType('Description (optional)', savedQueryDescription); cy.react('EuiButtonDisplay').contains('Save').click(); - - // visit Status results - cy.react('EuiTab', { props: { id: 'status' } }).click(); - cy.react('EuiTableRow').should('have.lengthOf', 1); - // cy.contains('Successful').siblings().contains(1); + cy.contains('Successfully saved'); // play saved query cy.contains('Saved queries').click(); diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts index e602c0228f886..6a3660494d181 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts @@ -73,7 +73,10 @@ function createRule(shouldWriteAlerts: boolean = true) { scheduleActions, } as any; }, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), }; diff --git a/x-pack/plugins/security/server/authentication/authentication_service.ts b/x-pack/plugins/security/server/authentication/authentication_service.ts index 50797c821e884..c22ac5fceecb6 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.ts @@ -335,7 +335,10 @@ export class AuthenticationService { loggers, clusterClient, basePath: http.basePath, - config: { authc: config.authc }, + config: { + authc: config.authc, + accessAgreement: config.accessAgreement, + }, getCurrentUser, featureUsageService, userProfileService, diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts index f5924d4d3cdab..1c62b5ae44dd9 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.test.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts @@ -48,14 +48,21 @@ function getMockOptions({ providers, http = {}, selector, + accessAgreementMessage, }: { providers?: Record | string[]; http?: Partial; selector?: AuthenticatorOptions['config']['authc']['selector']; + accessAgreementMessage?: string; } = {}) { const auditService = auditServiceMock.create(); auditLogger = auditLoggerMock.create(); auditService.asScoped.mockReturnValue(auditLogger); + + const accessAgreementObj = accessAgreementMessage + ? { accessAgreement: { message: accessAgreementMessage } } + : null; + return { audit: auditService, getCurrentUser: jest.fn(), @@ -65,7 +72,10 @@ function getMockOptions({ loggers: loggingSystemMock.create(), getServerBaseURL: jest.fn(), config: createConfig( - ConfigSchema.validate({ authc: { selector, providers, http } }), + ConfigSchema.validate({ + authc: { selector, providers, http }, + ...accessAgreementObj, + }), loggingSystemMock.create().get(), { isTLSEnabled: false } ), @@ -477,7 +487,7 @@ describe('Authenticator', () => { expect(mockOptions.userProfileService.activate).toHaveBeenCalledWith(userProfileGrant); }); - it('does not activate profiles for the Elastic Cloud users even if profile grant is provided', async () => { + it('activates profiles for the Elastic Cloud users if profile grant is provided', async () => { const user = mockAuthenticatedUser({ elastic_cloud_user: true }); const request = httpServerMock.createKibanaRequest(); const authorization = `Basic ${Buffer.from('foo:bar').toString('base64')}`; @@ -499,12 +509,14 @@ describe('Authenticator', () => { expect(mockOptions.session.create).toHaveBeenCalledTimes(1); expect(mockOptions.session.create).toHaveBeenCalledWith(request, { + userProfileId: 'some-profile-uid', username: user.username, provider: mockSessVal.provider, state: { authorization }, }); expectAuditEvents({ action: 'user_login', outcome: 'success' }); - expect(mockOptions.userProfileService.activate).not.toHaveBeenCalled(); + expect(mockOptions.userProfileService.activate).toHaveBeenCalledTimes(1); + expect(mockOptions.userProfileService.activate).toHaveBeenCalledWith(userProfileGrant); }); it('returns `notHandled` if login attempt is targeted to not configured provider.', async () => { @@ -1919,6 +1931,39 @@ describe('Authenticator', () => { ); expect(auditLogger.log).not.toHaveBeenCalled(); }); + + it('redirects to global Access Agreement when provider specific Access Agreement is not configured.', async () => { + mockOptions = getMockOptions({ + providers: { + basic: { basic1: { order: 0 } }, + }, + accessAgreementMessage: 'Foo', + }); + + mockOptions.license.getFeatures.mockReturnValue({ + allowAccessAgreement: true, + } as SecurityLicenseFeatures); + + authenticator = new Authenticator(mockOptions); + + mockOptions.session.get.mockResolvedValue(mockSessVal); + mockOptions.session.extend.mockResolvedValue(mockSessVal); + + mockBasicAuthenticationProvider.authenticate.mockResolvedValue( + AuthenticationResult.succeeded(mockUser, { + authResponseHeaders: { 'WWW-Authenticate': 'Negotiate' }, + }) + ); + + const request = httpServerMock.createKibanaRequest(); + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.redirectTo( + '/mock-server-basepath/security/access_agreement?next=%2Fmock-server-basepath%2Fpath', + { user: mockUser, authResponseHeaders: { 'WWW-Authenticate': 'Negotiate' } } + ) + ); + expect(auditLogger.log).not.toHaveBeenCalled(); + }); }); describe('with Overwritten Session', () => { diff --git a/x-pack/plugins/security/server/authentication/authenticator.ts b/x-pack/plugins/security/server/authentication/authenticator.ts index 3457ae42aa4dd..735224fd83720 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.ts @@ -82,7 +82,7 @@ export interface AuthenticatorOptions { featureUsageService: SecurityFeatureUsageServiceStart; userProfileService: UserProfileServiceStartInternal; getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; - config: Pick; + config: Pick; basePath: IBasePath; license: SecurityLicense; loggers: LoggerFactory; @@ -721,10 +721,8 @@ export class Authenticator { // If authentication result includes user profile grant, we should try to activate user profile for this user and // store user profile identifier in the session value. - // IMPORTANT: We don't activate profiles for the Elastic Cloud managed users until Cloud supports stable user - // profile identifiers. - const shouldActivateProfile = - authenticationResult.userProfileGrant && !authenticationResult.user?.elastic_cloud_user; + const shouldActivateProfile = authenticationResult.userProfileGrant; + if (shouldActivateProfile) { this.logger.debug(`Activating profile for "${authenticationResult.user?.username}".`); userProfileId = ( @@ -833,21 +831,27 @@ export class Authenticator { * @param sessionValue Current session value if any. */ private shouldRedirectToAccessAgreement(sessionValue: SessionValue | null) { - // Request should be redirected to Access Agreement UI only if all following conditions are met: - // 1. Request can be redirected (not API call) - // 2. Request is authenticated, but user hasn't acknowledged access agreement in the current - // session yet (based on the flag we store in the session) - // 3. Request is authenticated by the provider that has `accessAgreement` configured - // 4. Current license allows access agreement - // 5. And it's not a request to the Access Agreement UI itself - return ( - sessionValue != null && - !sessionValue.accessAgreementAcknowledged && - (this.options.config.authc.providers as Record)[sessionValue.provider.type]?.[ - sessionValue.provider.name - ]?.accessAgreement && - this.options.license.getFeatures().allowAccessAgreement - ); + // If user doesn't have an active session or if they already acknowledged + // access agreement (based on the flag we store in the session) - bail out. + if (sessionValue == null || sessionValue.accessAgreementAcknowledged) { + return false; + } + + // If access agreement is neither enabled globally (for all providers) + // nor for the provider that authenticated user request - bail out. + const providerConfig = (this.options.config.authc.providers as Record)[ + sessionValue.provider.type + ]?.[sessionValue.provider.name]; + + if ( + !this.options.config.accessAgreement?.message && + !providerConfig?.accessAgreement?.message + ) { + return false; + } + + // Check if the current license allows access agreement. + return this.options.license.getFeatures().allowAccessAgreement; } /** @@ -876,6 +880,7 @@ export class Authenticator { const isUpdatedSessionAuthenticated = isSessionAuthenticated(sessionUpdateResult?.value); let preAccessRedirectURL; + if (isUpdatedSessionAuthenticated && sessionUpdateResult?.overwritten) { this.logger.debug('Redirecting user to the overwritten session UI.'); preAccessRedirectURL = `${this.options.basePath.serverBasePath}${OVERWRITTEN_SESSION_ROUTE}`; diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index f0db13bc773d6..1c50c82d3a0f6 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -2204,4 +2204,30 @@ describe('createConfig()', () => { `); }); }); + + describe('Global Access Agreement', () => { + it('should require `message` for globally configured `accessAgreement`', () => { + expect(() => { + createConfig( + ConfigSchema.validate({ + accessAgreement: {}, + }), + loggingSystemMock.create().get(), + { isTLSEnabled: true } + ); + }).toThrow('[accessAgreement.message]: expected value of type [string] but got [undefined]'); + }); + + it('should accept string `message` for globally configured `accessAgreement`', () => { + expect( + createConfig( + ConfigSchema.validate({ + accessAgreement: { message: 'Foo' }, + }), + loggingSystemMock.create().get(), + { isTLSEnabled: true } + )?.accessAgreement?.message + ).toEqual('Foo'); + }); + }); }); diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index 8d31e8ec95bfd..aa7e3c0964ba8 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -83,6 +83,7 @@ function getUniqueProviderSchema>>( } type ProvidersConfigType = TypeOf; + const providersConfigSchema = schema.object( { basic: getUniqueProviderSchema('basic', { @@ -235,6 +236,7 @@ export const ConfigSchema = schema.object({ hostname: schema.maybe(schema.string({ hostname: true })), port: schema.maybe(schema.number({ min: 0, max: 65535 })), }), + accessAgreement: schema.maybe(schema.object({ message: schema.string() })), authc: schema.object({ selector: schema.object({ enabled: schema.maybe(schema.boolean()) }), providers: schema.oneOf([schema.arrayOf(schema.string()), providersConfigSchema], { @@ -306,6 +308,7 @@ export function createConfig( } let secureCookies = config.secureCookies; + if (!isTLSEnabled) { if (secureCookies) { logger.warn( @@ -322,6 +325,7 @@ export function createConfig( } const isUsingLegacyProvidersFormat = Array.isArray(config.authc.providers); + const providers = ( isUsingLegacyProvidersFormat ? [...new Set(config.authc.providers as Array)].reduce( @@ -332,6 +336,7 @@ export function createConfig( ? { enabled: true, showInSelector: true, order, ...config.authc[providerType] } : { enabled: true, showInSelector: true, order }, }; + return legacyProviders; }, {} as Record @@ -346,16 +351,19 @@ export function createConfig( order: number; hasAccessAgreement: boolean; }> = []; + for (const [type, providerGroup] of Object.entries(providers)) { for (const [name, { enabled, order, accessAgreement }] of Object.entries(providerGroup ?? {})) { if (!enabled) { delete providerGroup![name]; } else { + const hasAccessAgreement: boolean = !!accessAgreement?.message; + sortedProviders.push({ type: type as any, name, order, - hasAccessAgreement: !!accessAgreement?.message, + hasAccessAgreement, }); } } @@ -391,6 +399,7 @@ export function createConfig( max: 10, }, } as AppenderConfigType); + return { ...config, audit: { diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts index 9b4bdb4d4329d..5635dceace0b6 100644 --- a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts +++ b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts @@ -31,6 +31,7 @@ describe('Access agreement view routes', () => { let session: jest.Mocked>; let license: jest.Mocked; let mockContext: SecurityRequestHandlerContext; + beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); router = routeParamsMock.router; @@ -138,7 +139,7 @@ describe('Access agreement view routes', () => { }); }); - it('returns non-empty `accessAgreement` only if it is configured.', async () => { + it('returns non-empty provider specific `accessAgreement` only if it is configured.', async () => { const request = httpServerMock.createKibanaRequest(); config.authc = routeDefinitionParamsMock.create({ @@ -172,5 +173,42 @@ describe('Access agreement view routes', () => { }); } }); + + it('returns global `accessAgreement` when provider specific `accessAgreement` is not configured', async () => { + const request = httpServerMock.createKibanaRequest(); + + config.authc = routeDefinitionParamsMock.create({ + authc: { + providers: { + basic: { basic1: { order: 0 } }, + saml: { + saml1: { + order: 1, + realm: 'realm1', + accessAgreement: { message: 'Some access agreement' }, + }, + }, + }, + }, + }).config.authc; + + config.accessAgreement = { message: 'Foo' }; + + const cases: Array<[AuthenticationProvider, string]> = [ + [{ type: 'basic', name: 'basic1' }, 'Foo'], + [{ type: 'saml', name: 'saml1' }, 'Some access agreement'], + [{ type: 'unknown-type', name: 'unknown-name' }, 'Foo'], + ]; + + for (const [sessionProvider, expectedAccessAgreement] of cases) { + session.get.mockResolvedValue(sessionMock.createValue({ provider: sessionProvider })); + + await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({ + options: { body: { accessAgreement: expectedAccessAgreement } }, + payload: { accessAgreement: expectedAccessAgreement }, + status: 200, + }); + } + }); }); }); diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.ts b/x-pack/plugins/security/server/routes/views/access_agreement.ts index 83e640f43dee9..49a3984e181ac 100644 --- a/x-pack/plugins/security/server/routes/views/access_agreement.ts +++ b/x-pack/plugins/security/server/routes/views/access_agreement.ts @@ -47,12 +47,23 @@ export function defineAccessAgreementRoutes({ // authenticated with the help of HTTP authentication), that means we should safely check if // we have it and can get a corresponding configuration. const sessionValue = await getSession().get(request); - const accessAgreement = - (sessionValue && + + let accessAgreement = ''; + + if (sessionValue) { + const providerSpecificAccessAgreement = config.authc.providers[ sessionValue.provider.type as keyof ConfigType['authc']['providers'] - ]?.[sessionValue.provider.name]?.accessAgreement?.message) || - ''; + ]?.[sessionValue.provider.name]?.accessAgreement?.message; + + const globalAccessAgreement = config.accessAgreement?.message; + + if (providerSpecificAccessAgreement) { + accessAgreement = providerSpecificAccessAgreement; + } else if (globalAccessAgreement) { + accessAgreement = globalAccessAgreement; + } + } return response.ok({ body: { accessAgreement } }); }) diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts index 15a0713d80326..286aed84527af 100644 --- a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts @@ -200,6 +200,37 @@ describe('Security UsageCollector', () => { }); describe('access agreement', () => { + it('reports if the global access agreement message is configured', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + accessAgreement: { message: 'Bar' }, + authc: { + providers: { + saml: { + saml1: { + realm: 'foo', + order: 1, + }, + }, + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(collectorFetchContext); + + expect(usage).toEqual({ + ...DEFAULT_USAGE, + accessAgreementEnabled: true, + enabledAuthProviders: ['saml'], + }); + }); + it('reports if the access agreement message is configured for any provider', async () => { const config = createSecurityConfig( ConfigSchema.validate({ diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts index 4050e70bbcfed..d33ff8617e453 100644 --- a/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts @@ -161,7 +161,8 @@ export function registerSecurityUsageCollector({ usageCollection, config, licens ]; const accessAgreementEnabled = allowAccessAgreement && - config.authc.sortedProviders.some((provider) => provider.hasAccessAgreement); + (!!config.accessAgreement?.message || + config.authc.sortedProviders.some((provider) => provider.hasAccessAgreement)); const httpAuthSchemes = config.authc.http.schemes.filter((scheme) => WELL_KNOWN_AUTH_SCHEMES.includes(scheme.toLowerCase()) diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts index c0975925d480c..aa07dfcbba8b2 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts @@ -8,7 +8,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { - enumeration, IsoDateString, NonEmptyString, PositiveInteger, @@ -359,75 +358,3 @@ export const privilege = t.type({ }); export type Privilege = t.TypeOf; - -export enum BulkAction { - 'enable' = 'enable', - 'disable' = 'disable', - 'export' = 'export', - 'delete' = 'delete', - 'duplicate' = 'duplicate', - 'edit' = 'edit', -} - -export const bulkAction = enumeration('BulkAction', BulkAction); - -export enum BulkActionEditType { - 'add_tags' = 'add_tags', - 'delete_tags' = 'delete_tags', - 'set_tags' = 'set_tags', - 'add_index_patterns' = 'add_index_patterns', - 'delete_index_patterns' = 'delete_index_patterns', - 'set_index_patterns' = 'set_index_patterns', - 'set_timeline' = 'set_timeline', -} - -const bulkActionEditPayloadTags = t.type({ - type: t.union([ - t.literal(BulkActionEditType.add_tags), - t.literal(BulkActionEditType.delete_tags), - t.literal(BulkActionEditType.set_tags), - ]), - value: tags, -}); - -export type BulkActionEditPayloadTags = t.TypeOf; - -const bulkActionEditPayloadIndexPatterns = t.intersection([ - t.type({ - type: t.union([ - t.literal(BulkActionEditType.add_index_patterns), - t.literal(BulkActionEditType.delete_index_patterns), - t.literal(BulkActionEditType.set_index_patterns), - ]), - value: index, - }), - t.exact(t.partial({ overwrite_data_views: t.boolean })), -]); - -export type BulkActionEditPayloadIndexPatterns = t.TypeOf< - typeof bulkActionEditPayloadIndexPatterns ->; - -const bulkActionEditPayloadTimeline = t.type({ - type: t.literal(BulkActionEditType.set_timeline), - value: t.type({ - timeline_id, - timeline_title, - }), -}); - -export type BulkActionEditPayloadTimeline = t.TypeOf; - -export const bulkActionEditPayload = t.union([ - bulkActionEditPayloadTags, - bulkActionEditPayloadIndexPatterns, - bulkActionEditPayloadTimeline, -]); - -export type BulkActionEditPayload = t.TypeOf; - -export type BulkActionEditForRuleAttributes = BulkActionEditPayloadTags; - -export type BulkActionEditForRuleParams = - | BulkActionEditPayloadIndexPatterns - | BulkActionEditPayloadTimeline; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.mock.ts index b3988568a4765..1768acc8dfbfa 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.mock.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { BulkAction, BulkActionEditType } from '../common/schemas'; +import { BulkAction, BulkActionEditType } from './perform_bulk_action_schema'; import type { PerformBulkActionSchema } from './perform_bulk_action_schema'; export const getPerformBulkActionSchemaMock = (): PerformBulkActionSchema => ({ diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.test.ts index a1f6122a2ef35..f6de8a29cc90d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.test.ts @@ -6,10 +6,13 @@ */ import type { PerformBulkActionSchema } from './perform_bulk_action_schema'; -import { performBulkActionSchema } from './perform_bulk_action_schema'; +import { + performBulkActionSchema, + BulkAction, + BulkActionEditType, +} from './perform_bulk_action_schema'; import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; import { left } from 'fp-ts/lib/Either'; -import { BulkAction, BulkActionEditType } from '../common/schemas'; const retrieveValidationMessage = (payload: unknown) => { const decoded = performBulkActionSchema.decode(payload); @@ -343,12 +346,12 @@ describe('perform_bulk_action_schema', () => { const message = retrieveValidationMessage(payload); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "edit" supplied to "action"', - 'Invalid value "set_timeline" supplied to "edit,type"', - 'Invalid value "{"timeline_title":"Test timeline title"}" supplied to "edit,value"', - 'Invalid value "undefined" supplied to "edit,value,timeline_id"', - ]); + expect(getPaths(left(message.errors))).toEqual( + expect.arrayContaining([ + 'Invalid value "{"timeline_title":"Test timeline title"}" supplied to "edit,value"', + 'Invalid value "undefined" supplied to "edit,value,timeline_id"', + ]) + ); expect(message.schema).toEqual({}); }); @@ -373,5 +376,163 @@ describe('perform_bulk_action_schema', () => { expect(message.schema).toEqual(payload); }); }); + + describe('rule actions', () => { + test('invalid request: invalid rule actions payload', () => { + const payload = { + query: 'name: test', + action: BulkAction.edit, + [BulkAction.edit]: [{ type: BulkActionEditType.add_rule_actions, value: [] }], + }; + + const message = retrieveValidationMessage(payload); + + expect(getPaths(left(message.errors))).toEqual( + expect.arrayContaining(['Invalid value "[]" supplied to "edit,value"']) + ); + expect(message.schema).toEqual({}); + }); + + test('invalid request: missing throttle in payload', () => { + const payload = { + query: 'name: test', + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + actions: [], + }, + }, + ], + }; + + const message = retrieveValidationMessage(payload); + + expect(getPaths(left(message.errors))).toEqual( + expect.arrayContaining(['Invalid value "undefined" supplied to "edit,value,throttle"']) + ); + expect(message.schema).toEqual({}); + }); + + test('invalid request: missing actions in payload', () => { + const payload = { + query: 'name: test', + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + throttle: '1h', + }, + }, + ], + }; + + const message = retrieveValidationMessage(payload); + + expect(getPaths(left(message.errors))).toEqual( + expect.arrayContaining(['Invalid value "undefined" supplied to "edit,value,actions"']) + ); + expect(message.schema).toEqual({}); + }); + + test('invalid request: invalid action_type_id property in actions array', () => { + const payload = { + query: 'name: test', + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + throttle: '1h', + actions: [ + { + action_type_id: '.webhook', + group: 'default', + id: '458a50e0-1a28-11ed-9098-47fd8e1f3345', + params: { + body: { + rule_id: '{{rule.id}}', + }, + }, + }, + ], + }, + }, + ], + }; + + const message = retrieveValidationMessage(payload); + expect(getPaths(left(message.errors))).toEqual( + expect.arrayContaining(['invalid keys "action_type_id"']) + ); + expect(message.schema).toEqual({}); + }); + + test('valid request: add_rule_actions edit action', () => { + const payload: PerformBulkActionSchema = { + query: 'name: test', + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + throttle: '1h', + actions: [ + { + group: 'default', + id: '458a50e0-1a28-11ed-9098-47fd8e1f3345', + params: { + body: { + rule_id: '{{rule.id}}', + }, + }, + }, + ], + }, + }, + ], + }; + + const message = retrieveValidationMessage(payload); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('valid request: set_rule_actions edit action', () => { + const payload: PerformBulkActionSchema = { + query: 'name: test', + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_rule_actions, + value: { + throttle: '1h', + actions: [ + { + group: 'default', + id: '458a50e0-1a28-11ed-9098-47fd8e1f3345', + params: { + documents: [ + { + rule_id: '{{rule.id}}', + }, + ], + }, + }, + ], + }, + }, + ], + }; + + const message = retrieveValidationMessage(payload); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts index fa33e75e236fd..58675bf3c0d99 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts @@ -6,8 +6,124 @@ */ import * as t from 'io-ts'; -import { NonEmptyArray } from '@kbn/securitysolution-io-ts-types'; -import { BulkAction, queryOrUndefined, bulkActionEditPayload } from '../common/schemas'; +import { NonEmptyArray, enumeration } from '@kbn/securitysolution-io-ts-types'; + +import { + throttle, + action_group as actionGroup, + action_params as actionParams, + action_id as actionId, +} from '@kbn/securitysolution-io-ts-alerting-types'; + +import { queryOrUndefined, tags, index, timeline_id, timeline_title } from '../common/schemas'; + +export enum BulkAction { + 'enable' = 'enable', + 'disable' = 'disable', + 'export' = 'export', + 'delete' = 'delete', + 'duplicate' = 'duplicate', + 'edit' = 'edit', +} + +export const bulkAction = enumeration('BulkAction', BulkAction); + +export enum BulkActionEditType { + 'add_tags' = 'add_tags', + 'delete_tags' = 'delete_tags', + 'set_tags' = 'set_tags', + 'add_index_patterns' = 'add_index_patterns', + 'delete_index_patterns' = 'delete_index_patterns', + 'set_index_patterns' = 'set_index_patterns', + 'set_timeline' = 'set_timeline', + 'add_rule_actions' = 'add_rule_actions', + 'set_rule_actions' = 'set_rule_actions', +} + +const bulkActionEditPayloadTags = t.type({ + type: t.union([ + t.literal(BulkActionEditType.add_tags), + t.literal(BulkActionEditType.delete_tags), + t.literal(BulkActionEditType.set_tags), + ]), + value: tags, +}); + +export type BulkActionEditPayloadTags = t.TypeOf; + +const bulkActionEditPayloadIndexPatterns = t.intersection([ + t.type({ + type: t.union([ + t.literal(BulkActionEditType.add_index_patterns), + t.literal(BulkActionEditType.delete_index_patterns), + t.literal(BulkActionEditType.set_index_patterns), + ]), + value: index, + }), + t.exact(t.partial({ overwrite_data_views: t.boolean })), +]); + +export type BulkActionEditPayloadIndexPatterns = t.TypeOf< + typeof bulkActionEditPayloadIndexPatterns +>; + +const bulkActionEditPayloadTimeline = t.type({ + type: t.literal(BulkActionEditType.set_timeline), + value: t.type({ + timeline_id, + timeline_title, + }), +}); + +export type BulkActionEditPayloadTimeline = t.TypeOf; + +/** + * per rulesClient.bulkEdit rules actions operation contract (x-pack/plugins/alerting/server/rules_client/rules_client.ts) + * normalized rule action object is expected (NormalizedAlertAction) as value for the edit operation + */ +const normalizedRuleAction = t.exact( + t.type({ + group: actionGroup, + id: actionId, + params: actionParams, + }) +); + +const bulkActionEditPayloadRuleActions = t.type({ + type: t.union([ + t.literal(BulkActionEditType.add_rule_actions), + t.literal(BulkActionEditType.set_rule_actions), + ]), + value: t.type({ + throttle, + actions: t.array(normalizedRuleAction), + }), +}); + +export type BulkActionEditPayloadRuleActions = t.TypeOf; + +export const bulkActionEditPayload = t.union([ + bulkActionEditPayloadTags, + bulkActionEditPayloadIndexPatterns, + bulkActionEditPayloadTimeline, + bulkActionEditPayloadRuleActions, +]); + +export type BulkActionEditPayload = t.TypeOf; + +/** + * actions that modify rules attributes + */ +export type BulkActionEditForRuleAttributes = + | BulkActionEditPayloadTags + | BulkActionEditPayloadRuleActions; + +/** + * actions that modify rules params + */ +export type BulkActionEditForRuleParams = + | BulkActionEditPayloadIndexPatterns + | BulkActionEditPayloadTimeline; export const performBulkActionSchema = t.intersection([ t.exact( diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index e8c0ee57241fd..cf0222e6f1faa 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -43,7 +43,7 @@ export const allowedExperimentalValues = Object.freeze({ /** * Enables the insights module for related alerts by process ancestry */ - insightsRelatedAlertsByProcessAncestry: false, + insightsRelatedAlertsByProcessAncestry: true, /** * Enables extended rule execution logging to Event Log. When this setting is enabled: diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index dfc26932e8d79..3153f5546ed91 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -67,11 +67,12 @@ import type { } from './cti'; import type { - RiskScoreStrategyResponse, RiskQueries, - RiskScoreRequestOptions, KpiRiskScoreStrategyResponse, KpiRiskScoreRequestOptions, + HostsRiskScoreStrategyResponse, + UsersRiskScoreStrategyResponse, + RiskScoreRequestOptions, } from './risk_score'; import type { UsersQueries } from './users'; import type { UserDetailsRequestOptions, UserDetailsStrategyResponse } from './users/details'; @@ -185,8 +186,10 @@ export type StrategyResponseType = T extends HostsQ ? CtiEventEnrichmentStrategyResponse : T extends CtiQueries.dataSource ? CtiDataSourceStrategyResponse - : T extends RiskQueries.riskScore - ? RiskScoreStrategyResponse + : T extends RiskQueries.hostsRiskScore + ? HostsRiskScoreStrategyResponse + : T extends RiskQueries.usersRiskScore + ? UsersRiskScoreStrategyResponse : T extends RiskQueries.kpiRiskScore ? KpiRiskScoreStrategyResponse : never; @@ -247,7 +250,9 @@ export type StrategyRequestType = T extends HostsQu ? CtiEventEnrichmentRequestOptions : T extends CtiQueries.dataSource ? CtiDataSourceRequestOptions - : T extends RiskQueries.riskScore + : T extends RiskQueries.hostsRiskScore + ? RiskScoreRequestOptions + : T extends RiskQueries.usersRiskScore ? RiskScoreRequestOptions : T extends RiskQueries.kpiRiskScore ? KpiRiskScoreRequestOptions diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts index a09c8a1617240..2eee56f15f083 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts @@ -6,14 +6,12 @@ */ import type { IEsSearchRequest, IEsSearchResponse } from '@kbn/data-plugin/common'; -import type { FactoryQueryTypes } from '../..'; - import type { ESQuery } from '../../../../typed_json'; + import type { Inspect, Maybe, SortField, TimerangeInput } from '../../../common'; export interface RiskScoreRequestOptions extends IEsSearchRequest { defaultIndex: string[]; - factoryQueryType?: FactoryQueryTypes; timerange?: TimerangeInput; onlyLatest?: boolean; pagination?: { @@ -24,9 +22,16 @@ export interface RiskScoreRequestOptions extends IEsSearchRequest { filterQuery?: ESQuery | string | undefined; } -export interface RiskScoreStrategyResponse extends IEsSearchResponse { +export interface HostsRiskScoreStrategyResponse extends IEsSearchResponse { + inspect?: Maybe; + totalCount: number; + data: HostsRiskScore[] | undefined; +} + +export interface UsersRiskScoreStrategyResponse extends IEsSearchResponse { inspect?: Maybe; totalCount: number; + data: UsersRiskScore[] | undefined; } export interface RiskScore { diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts index afc964be58ac4..c6f651440edb9 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts @@ -7,6 +7,10 @@ import { RISKY_HOSTS_INDEX_PREFIX, RISKY_USERS_INDEX_PREFIX } from '../../../../constants'; +/** + * Make sure this aligns with the index in step 6, 9 in + * prebuilt_dev_tool_content/console_templates/enable_host_risk_score.console + */ export const getHostRiskIndex = (spaceId: string, onlyLatest: boolean = true): string => { return `${RISKY_HOSTS_INDEX_PREFIX}${onlyLatest ? 'latest_' : ''}${spaceId}`; }; @@ -24,7 +28,8 @@ export const buildUserNamesFilter = (userNames: string[]) => { }; export enum RiskQueries { - riskScore = 'riskScore', + hostsRiskScore = 'hostsRiskScore', + usersRiskScore = 'usersRiskScore', kpiRiskScore = 'kpiRiskScore', } diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.ts index cd12fab70a891..2bcd2910a3c1a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.ts @@ -14,9 +14,17 @@ export const createSpace = (id: string) => { name: id, }, headers: { 'kbn-xsrf': 'cypress-creds' }, + }).then((response) => { + expect(response.status).equal(200); }); }; export const removeSpace = (id: string) => { - cy.request(`/api/spaces/space/${id}`); + cy.request({ + method: 'delete', + url: `/api/spaces/space/${id}`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }).then((response) => { + expect(response.status).equal(204); + }); }; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx index a2bd3ce273ea4..f95bf9234cc16 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx @@ -14,7 +14,12 @@ import { TestProviders } from '../../../mock'; import { useAlertPrevalenceFromProcessTree } from '../../../containers/alerts/use_alert_prevalence_from_process_tree'; import { RelatedAlertsByProcessAncestry } from './related_alerts_by_process_ancestry'; import { ACTION_INVESTIGATE_IN_TIMELINE } from '../../../../detections/components/alerts_table/translations'; -import { PROCESS_ANCESTRY, PROCESS_ANCESTRY_COUNT, PROCESS_ANCESTRY_ERROR } from './translations'; +import { + PROCESS_ANCESTRY, + PROCESS_ANCESTRY_COUNT, + PROCESS_ANCESTRY_ERROR, + PROCESS_ANCESTRY_EMPTY, +} from './translations'; jest.mock('../../../containers/alerts/use_alert_prevalence_from_process_tree', () => ({ useAlertPrevalenceFromProcessTree: jest.fn(), @@ -110,4 +115,23 @@ describe('RelatedAlertsByProcessAncestry', () => { ).toBeInTheDocument(); }); }); + + it('renders a special message when there are no alerts to display', async () => { + mockUseAlertPrevalenceFromProcessTree.mockReturnValue({ + loading: false, + error: false, + alertIds: [] as string[], + }); + + render( + + + + ); + + userEvent.click(screen.getByText(PROCESS_ANCESTRY)); + await waitFor(() => { + expect(screen.getByText(PROCESS_ANCESTRY_EMPTY)).toBeInTheDocument(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx index bea416ff51ec6..330cb7ae113b3 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx @@ -6,7 +6,7 @@ */ import React, { useMemo, useCallback, useEffect, useState } from 'react'; -import { EuiBetaBadge, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; import type { DataProvider } from '../../../../../common/types'; import { TimelineId } from '../../../../../common/types/timeline'; @@ -23,7 +23,6 @@ import { PROCESS_ANCESTRY_EMPTY, PROCESS_ANCESTRY_ERROR, } from './translations'; -import { BETA } from '../../../translations'; interface Props { data: TimelineEventsDetailsItem; @@ -113,7 +112,6 @@ export const RelatedAlertsByProcessAncestry = React.memo( } renderContent={renderContent} onToggle={onToggle} - extraAction={} /> ); } @@ -192,7 +190,7 @@ const ActualRelatedAlertsByProcessAncestry: React.FC<{ {ACTION_INVESTIGATE_IN_TIMELINE} diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx index 49dbc7ec674ca..8b0b308829c3d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback } from 'react'; -import { EuiBetaBadge, EuiSpacer } from '@elastic/eui'; +import { EuiSpacer } from '@elastic/eui'; import type { BrowserFields } from '../../../containers/source'; import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline'; @@ -19,7 +19,6 @@ import { SimpleAlertTable } from './simple_alert_table'; import { getEnrichedFieldInfo } from '../helpers'; import { ACTION_INVESTIGATE_IN_TIMELINE } from '../../../../detections/components/alerts_table/translations'; import { SESSION_LOADING, SESSION_EMPTY, SESSION_ERROR, SESSION_COUNT } from './translations'; -import { BETA } from '../../../translations'; interface Props { browserFields: BrowserFields; @@ -100,7 +99,6 @@ export const RelatedAlertsBySession = React.memo( state={state} text={getTextFromState(state, count)} renderContent={renderContent} - extraAction={} /> ); } 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 220d289b0c9d1..00dd75222cafb 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 @@ -153,6 +153,25 @@ describe('useSearchStrategy', () => { expect(mockAddToastError).toBeCalledWith(error, { title: errorMessage }); }); + it('does not show toast error if showErrorToast = false', () => { + const error = 'test error'; + const errorMessage = 'error message title'; + (useObservable as jest.Mock).mockReturnValue({ + ...useObservableHookResult, + error, + }); + + renderHook(() => + useSearchStrategy({ + ...userSearchStrategyProps, + showErrorToast: false, + errorMessage, + }) + ); + + expect(mockAddToastError).not.toBeCalled(); + }); + it('start should be called when search is called ', () => { const start = jest.fn(); 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 39e3d6a8eebd8..65175f00074c8 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 @@ -94,6 +94,7 @@ export const useSearchStrategy = ({ initialResult, errorMessage, abort = false, + showErrorToast = true, }: { factoryQueryType: QueryType; /** @@ -108,6 +109,10 @@ export const useSearchStrategy = ({ * When the flag switches from `false` to `true`, it will abort any ongoing request. */ abort?: boolean; + /** + * Show error toast when error occurs on search complete + */ + showErrorToast?: boolean; }) => { const abortCtrl = useRef(new AbortController()); const refetch = useRef(noop); @@ -121,12 +126,12 @@ export const useSearchStrategy = ({ >(search); useEffect(() => { - if (error != null && !(error instanceof AbortError)) { + if (showErrorToast && error != null && !(error instanceof AbortError)) { addError(error, { title: errorMessage ?? i18n.DEFAULT_ERROR_SEARCH_STRATEGY(factoryQueryType), }); } - }, [addError, error, errorMessage, factoryQueryType]); + }, [addError, error, errorMessage, factoryQueryType, showErrorToast]); const searchCb = useCallback>( (request) => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx index b8e423827edce..beb8e8365d74e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx @@ -16,7 +16,7 @@ import { noop } from 'lodash'; import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { APP_UI_ID, SecurityPageName } from '../../../../../common/constants'; -import { BulkAction } from '../../../../../common/detection_engine/schemas/common'; +import { BulkAction } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { getRulesUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { useBoolState } from '../../../../common/hooks/use_bool_state'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx index 574f4ec166193..6c924a68da4a1 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSwitch } from '@elasti import { noop } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; import styled from 'styled-components'; -import { BulkAction } from '../../../../../common/detection_engine/schemas/common'; +import { BulkAction } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { SINGLE_RULE_ACTIONS } from '../../../../common/lib/apm/user_actions'; import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts new file mode 100644 index 0000000000000..57a7dd50a370e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +import type { ActionTypeRegistryContract } from '@kbn/triggers-actions-ui-plugin/public'; +import { validateRuleActionsField } from '../../../containers/detection_engine/rules/validate_rule_actions_field'; + +import type { FormSchema } from '../../../../shared_imports'; +import type { ActionsStepRule } from '../../../pages/detection_engine/rules/types'; + +export const getSchema = ({ + actionTypeRegistry, +}: { + actionTypeRegistry: ActionTypeRegistryContract; +}): FormSchema => ({ + actions: { + validations: [ + { + validator: validateRuleActionsField(actionTypeRegistry), + }, + ], + }, + enabled: {}, + kibanaSiemAppUrl: {}, + throttle: { + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel', + { + defaultMessage: 'Actions frequency', + } + ), + helpText: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText', + { + defaultMessage: + 'Select when automated actions should be performed if a rule evaluates as true.', + } + ), + }, +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index d8264794f1df8..b967304d57349 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -31,7 +31,7 @@ import { } from '../throttle_select_field'; import { RuleActionsField } from '../rule_actions_field'; import { useKibana } from '../../../../common/lib/kibana'; -import { getSchema } from './schema'; +import { getSchema } from './get_schema'; import * as I18n from './translations'; import { APP_UI_ID } from '../../../../../common/constants'; import { useManageCaseAction } from './use_manage_case_action'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index f0d3d7b7d351e..d467c3af05f8f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -6,7 +6,6 @@ */ import { i18n } from '@kbn/i18n'; -import { startCase } from 'lodash/fp'; export const COMPLETE_WITHOUT_ENABLING = i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle', @@ -29,14 +28,3 @@ export const NO_ACTIONS_READ_PERMISSIONS = i18n.translate( 'Cannot create rule actions. You do not have "Read" permissions for the "Actions" plugin.', } ); - -export const INVALID_MUSTACHE_TEMPLATE = (paramKey: string) => - i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage', - { - defaultMessage: '{key} is not valid mustache template', - values: { - key: startCase(paramKey), - }, - } - ); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts index 0e2f238aa527e..876a3a0a469a8 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts @@ -18,7 +18,7 @@ import { DETECTION_ENGINE_INSTALLED_INTEGRATIONS_URL, DETECTION_ENGINE_RULES_URL_FIND, } from '../../../../../common/constants'; -import type { BulkAction } from '../../../../../common/detection_engine/schemas/common'; +import type { BulkAction } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { FullResponseSchema, PreviewResponse, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts index eaf9b3288dc2d..45c89c307de4e 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts @@ -25,11 +25,11 @@ import { import { RuleExecutionSummary } from '../../../../../common/detection_engine/rule_monitoring'; +import type { SortOrder } from '../../../../../common/detection_engine/schemas/common'; import type { - SortOrder, - BulkAction, BulkActionEditPayload, -} from '../../../../../common/detection_engine/schemas/common'; + BulkAction, +} from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { alias_purpose as savedObjectResolveAliasPurpose, outcome as savedObjectResolveOutcome, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/index.tsx new file mode 100644 index 0000000000000..22d05080a408b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/index.tsx @@ -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 { validateRuleActionsField } from './validate_rule_actions_field'; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/translations.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/translations.ts new file mode 100644 index 0000000000000..6540f7071ccd6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/translations.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 { i18n } from '@kbn/i18n'; +import { startCase } from 'lodash/fp'; + +export const INVALID_MUSTACHE_TEMPLATE = (paramKey: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage', + { + defaultMessage: '{key} is not valid mustache template', + values: { + key: startCase(paramKey), + }, + } + ); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/utils.test.ts similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.test.ts rename to x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/utils.test.ts diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/utils.ts similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.ts rename to x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/utils.ts diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/schema.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/validate_rule_actions_field.test.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/schema.test.tsx rename to x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/validate_rule_actions_field.test.tsx index 58acba634311a..335d6faf631f5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/schema.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/validate_rule_actions_field.test.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import { validateSingleAction, validateRuleActionsField } from './schema'; +import { validateSingleAction, validateRuleActionsField } from './validate_rule_actions_field'; import { getActionTypeName, validateMustache, validateActionParams } from './utils'; import { actionTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/application/action_type_registry.mock'; -import type { FormHook } from '../../../../shared_imports'; +import type { FormHook } from '../../../../../shared_imports'; jest.mock('./utils'); -describe('stepRuleActions schema', () => { +describe('validate_rule_actions_field', () => { const actionTypeRegistry = actionTypeRegistryMock.create(); describe('validateSingleAction', () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/schema.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/validate_rule_actions_field.ts similarity index 62% rename from x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/schema.tsx rename to x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/validate_rule_actions_field.ts index efc34c7b4d13d..18aa758d0a499 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/validate_rule_actions_field/validate_rule_actions_field.ts @@ -7,19 +7,11 @@ /* istanbul ignore file */ -import { i18n } from '@kbn/i18n'; - import type { RuleAction, ActionTypeRegistryContract, } from '@kbn/triggers-actions-ui-plugin/public'; -import type { - FormSchema, - ValidationFunc, - ERROR_CODE, - ValidationError, -} from '../../../../shared_imports'; -import type { ActionsStepRule } from '../../../pages/detection_engine/rules/types'; +import type { ValidationFunc, ERROR_CODE, ValidationError } from '../../../../../shared_imports'; import { getActionTypeName, validateMustache, validateActionParams } from './utils'; export const validateSingleAction = async ( @@ -59,34 +51,3 @@ export const validateRuleActionsField = }; } }; - -export const getSchema = ({ - actionTypeRegistry, -}: { - actionTypeRegistry: ActionTypeRegistryContract; -}): FormSchema => ({ - actions: { - validations: [ - { - validator: validateRuleActionsField(actionTypeRegistry), - }, - ], - }, - enabled: {}, - kibanaSiemAppUrl: {}, - throttle: { - label: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel', - { - defaultMessage: 'Actions frequency', - } - ), - helpText: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText', - { - defaultMessage: - 'Select when automated actions should be performed if a rule evaluates as true.', - } - ), - }, -}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts index 28d3f4856579f..d80835209010f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts @@ -7,8 +7,8 @@ import type { NavigateToAppOptions } from '@kbn/core/public'; import { APP_UI_ID } from '../../../../../../common/constants'; -import type { BulkActionEditPayload } from '../../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkAction } from '../../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkAction } from '../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { HTTPError } from '../../../../../../common/detection_engine/types'; import { SecurityPageName } from '../../../../../app/types'; import { getEditRuleUrl } from '../../../../../common/components/link_to/redirect_to_detection_engine'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_dry_run_confirmation.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_dry_run_confirmation.tsx index 33d325b837d33..9207e0813ab70 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_dry_run_confirmation.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_dry_run_confirmation.tsx @@ -10,7 +10,7 @@ import { EuiConfirmModal } from '@elastic/eui'; import * as i18n from '../../translations'; import { BulkActionRuleErrorsList } from './bulk_action_rule_errors_list'; -import { BulkAction } from '../../../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkAction } from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { assertUnreachable } from '../../../../../../../common/utility_types'; import type { BulkActionForConfirmation, DryRunResult } from './types'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.test.tsx index 82b78c319c056..987b244dd9fc6 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.test.tsx @@ -13,7 +13,7 @@ import { render, screen } from '@testing-library/react'; import { BulkActionRuleErrorsList } from './bulk_action_rule_errors_list'; import { BulkActionsDryRunErrCode } from '../../../../../../../common/constants'; import type { DryRunResult } from './types'; -import { BulkAction } from '../../../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkAction } from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; const Wrapper: FC = ({ children }) => { return ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.tsx index 7f5b3ea3f74ee..8f0275000a4ef 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_action_rule_errors_list.tsx @@ -10,7 +10,7 @@ import { EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { BulkActionsDryRunErrCode } from '../../../../../../../common/constants'; -import { BulkAction } from '../../../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkAction } from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { DryRunResult, BulkActionForConfirmation } from './types'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx index 4a4d33c358b0e..db96a63f6245e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx @@ -7,12 +7,13 @@ import React from 'react'; -import type { BulkActionEditPayload } from '../../../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { IndexPatternsForm } from './forms/index_patterns_form'; import { TagsForm } from './forms/tags_form'; import { TimelineTemplateForm } from './forms/timeline_template_form'; +import { RuleActionsForm } from './forms/rule_actions_form'; interface BulkEditFlyoutProps { onClose: () => void; @@ -37,6 +38,10 @@ const BulkEditFlyoutComponent = ({ editAction, tags, ...props }: BulkEditFlyoutP case BulkActionEditType.set_timeline: return ; + case BulkActionEditType.add_rule_actions: + case BulkActionEditType.set_rule_actions: + return ; + default: return null; } diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx index 26a555b7de335..a4fafd8d21bfd 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx @@ -7,6 +7,7 @@ import type { FC } from 'react'; import React from 'react'; +import type { EuiFlyoutSize } from '@elastic/eui'; import { useGeneratedHtmlId, EuiFlyout, @@ -32,6 +33,7 @@ interface BulkEditFormWrapperProps { children: React.ReactNode; onClose: () => void; onSubmit: () => void; + flyoutSize?: EuiFlyoutSize; } const BulkEditFormWrapperComponent: FC = ({ @@ -41,6 +43,7 @@ const BulkEditFormWrapperComponent: FC = ({ children, onClose, onSubmit, + flyoutSize = 's', }) => { const simpleFlyoutTitleId = useGeneratedHtmlId({ prefix: 'RulesBulkEditForm', @@ -48,7 +51,7 @@ const BulkEditFormWrapperComponent: FC = ({ const { isValid } = form; return ( - +

{title}

diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/index_patterns_form.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/index_patterns_form.tsx index 2ce632be3ef3c..adb19e397027b 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/index_patterns_form.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/index_patterns_form.tsx @@ -14,8 +14,8 @@ import * as i18n from '../../../translations'; import { DEFAULT_INDEX_KEY } from '../../../../../../../../common/constants'; import { useKibana } from '../../../../../../../common/lib/kibana'; -import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { FormSchema } from '../../../../../../../shared_imports'; import { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/rule_actions_form.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/rule_actions_form.tsx new file mode 100644 index 0000000000000..8a9e52c9d5528 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/rule_actions_form.tsx @@ -0,0 +1,244 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useCallback, useMemo } from 'react'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { + RuleAction, + ActionTypeRegistryContract, +} from '@kbn/triggers-actions-ui-plugin/public'; +import type { FormSchema } from '../../../../../../../shared_imports'; +import { + useForm, + UseField, + FIELD_TYPES, + useFormData, + getUseField, + Field, +} from '../../../../../../../shared_imports'; +import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../../../../common/constants'; + +import { BulkEditFormWrapper } from './bulk_edit_form_wrapper'; +import { bulkAddRuleActions as i18n } from '../translations'; + +import { useKibana } from '../../../../../../../common/lib/kibana'; + +import { + ThrottleSelectField, + THROTTLE_OPTIONS, +} from '../../../../../../components/rules/throttle_select_field'; +import { getAllActionMessageParams } from '../../../helpers'; + +import { RuleActionsField } from '../../../../../../components/rules/rule_actions_field'; +import { validateRuleActionsField } from '../../../../../../containers/detection_engine/rules/validate_rule_actions_field'; + +const CommonUseField = getUseField({ component: Field }); + +export interface RuleActionsFormData { + throttle: string; + actions: RuleAction[]; + overwrite: boolean; +} + +const getFormSchema = ( + actionTypeRegistry: ActionTypeRegistryContract +): FormSchema => ({ + throttle: { + label: i18n.THROTTLE_LABEL, + helpText: i18n.THROTTLE_HELP_TEXT, + }, + actions: { + validations: [ + { + validator: validateRuleActionsField(actionTypeRegistry), + }, + ], + }, + overwrite: { + type: FIELD_TYPES.CHECKBOX, + label: i18n.OVERWRITE_LABEL, + }, +}); + +const defaultFormData: RuleActionsFormData = { + throttle: NOTIFICATION_THROTTLE_NO_ACTIONS, + actions: [], + overwrite: false, +}; + +interface RuleActionsFormProps { + rulesCount: number; + onClose: () => void; + onConfirm: (bulkActionEditPayload: BulkActionEditPayload) => void; +} + +const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleActionsFormProps) => { + const { + services: { + triggersActionsUi: { actionTypeRegistry }, + }, + } = useKibana(); + + const formSchema = useMemo(() => getFormSchema(actionTypeRegistry), [actionTypeRegistry]); + + const { form } = useForm({ + schema: formSchema, + defaultValue: defaultFormData, + }); + + const [{ overwrite, throttle }] = useFormData({ form, watch: ['overwrite', 'throttle'] }); + + const handleSubmit = useCallback(async () => { + const { data, isValid } = await form.submit(); + if (!isValid) { + return; + } + + const { actions = [], throttle: throttleToSubmit, overwrite: overwriteValue } = data; + const editAction = overwriteValue + ? BulkActionEditType.set_rule_actions + : BulkActionEditType.add_rule_actions; + + onConfirm({ + type: editAction, + value: { + actions: actions.map(({ actionTypeId, ...action }) => action), + throttle: throttleToSubmit, + }, + }); + }, [form, onConfirm]); + + const throttleFieldComponentProps = useMemo( + () => ({ + idAria: 'bulkEditRulesRuleActionThrottle', + dataTestSubj: 'bulkEditRulesRuleActionThrottle', + hasNoInitialSelection: false, + euiFieldProps: { + options: THROTTLE_OPTIONS, + }, + }), + [] + ); + + const messageVariables = useMemo(() => getAllActionMessageParams(), []); + + const showActionsSelect = throttle !== NOTIFICATION_THROTTLE_NO_ACTIONS; + + return ( + + + } + > +
    +
  • + +
  • +
  • + + + + ), + overwriteActionsCheckbox: ( + + + + ), + }} + /> +
  • +
  • {i18n.RULE_VARIABLES_DETAIL}
  • +
+
+ + + + + + {showActionsSelect && ( + <> + + + + )} + + + + {overwrite && ( + <> + + + + + + ), + }} + /> + + + )} +
+ ); +}; + +export const RuleActionsForm = React.memo(RuleActionsFormComponent); +RuleActionsForm.displayName = 'RuleActionsForm'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/tags_form.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/tags_form.tsx index 366115623d041..e53469e27a09a 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/tags_form.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/tags_form.tsx @@ -11,8 +11,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import * as i18n from '../../../translations'; import { caseInsensitiveSort } from '../../helpers'; -import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { FormSchema } from '../../../../../../../shared_imports'; import { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx index 3c5852926a5d7..6aa5a3100c100 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx @@ -11,8 +11,8 @@ import { EuiCallOut } from '@elastic/eui'; import type { FormSchema } from '../../../../../../../shared_imports'; import { useForm, UseField } from '../../../../../../../shared_imports'; import { PickTimeline } from '../../../../../../components/rules/pick_timeline'; -import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { BulkEditFormWrapper } from './bulk_edit_form_wrapper'; import { bulkApplyTimelineTemplate as i18n } from '../translations'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx index cce2d3e032a83..3c7fdd9cb7a8c 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx @@ -55,3 +55,42 @@ export const bulkApplyTimelineTemplate = { /> ), }; + +export const bulkAddRuleActions = { + FORM_TITLE: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle', + { + defaultMessage: 'Add rule actions', + } + ), + + OVERWRITE_LABEL: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel', + { + defaultMessage: 'Overwrite all selected rules actions', + } + ), + + THROTTLE_LABEL: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel', + { + defaultMessage: 'Actions frequency', + } + ), + + THROTTLE_HELP_TEXT: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText', + { + defaultMessage: + 'Select when automated actions should be performed if a rule evaluates as true.', + } + ), + + RULE_VARIABLES_DETAIL: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail', + { + defaultMessage: + 'Rule variables may affect only some of the rules you select, based on the rule types (for example, \\u007b\\u007bcontext.rule.threshold\\u007d\\u007d will only display values for threshold rules).', + } + ), +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/types.ts index 9041e83167469..d81c7c995a2fe 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/types.ts @@ -6,7 +6,7 @@ */ import type { BulkActionsDryRunErrCode } from '../../../../../../../common/constants'; -import type { BulkAction } from '../../../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkAction } from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; /** * Only 2 bulk actions are supported for for confirmation dry run modal: diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx index 9c5383dc1a693..84da6c6cc6882 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx @@ -14,11 +14,11 @@ import { useIsMounted } from '@kbn/securitysolution-hook-utils'; import type { Toast } from '@kbn/core/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import type { BulkActionEditPayload } from '../../../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { BulkAction, BulkActionEditType, -} from '../../../../../../../common/detection_engine/schemas/common/schemas'; +} from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { isMlRule } from '../../../../../../../common/machine_learning/helpers'; import { canEditRuleWithActions } from '../../../../../../common/utils/privileges'; import { useRulesTableContext } from '../rules_table/rules_table_context'; @@ -360,6 +360,16 @@ export const useBulkActions = ({ disabled: isEditDisabled, panel: 1, }, + { + key: i18n.BULK_ACTION_ADD_RULE_ACTIONS, + name: i18n.BULK_ACTION_ADD_RULE_ACTIONS, + 'data-test-subj': 'addRuleActionsBulk', + disabled: isEditDisabled, + onClick: handleBulkEdit(BulkActionEditType.add_rule_actions), + toolTipContent: missingActionPrivileges ? i18n.EDIT_RULE_SETTINGS_TOOLTIP : undefined, + toolTipPosition: 'right', + icon: undefined, + }, { key: i18n.BULK_ACTION_APPLY_TIMELINE_TEMPLATE, name: i18n.BULK_ACTION_APPLY_TIMELINE_TEMPLATE, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts index 28c4e4be608dc..5e14c0a57bf51 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts @@ -11,7 +11,7 @@ import { useMutation } from '@tanstack/react-query'; import type { BulkAction, BulkActionEditType, -} from '../../../../../../../common/detection_engine/schemas/common/schemas'; +} from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { BulkActionResponse } from '../../../../../containers/detection_engine/rules'; import { performBulkAction } from '../../../../../containers/detection_engine/rules'; import { computeDryRunPayload } from './utils/compute_dry_run_payload'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_edit_form_flyout.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_edit_form_flyout.ts index babdd1bfa536e..6d629ae1869b4 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_edit_form_flyout.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_edit_form_flyout.ts @@ -8,9 +8,9 @@ import { useState, useCallback, useRef } from 'react'; import { useAsyncConfirmation } from '../rules_table/use_async_confirmation'; import type { - BulkActionEditType, BulkActionEditPayload, -} from '../../../../../../../common/detection_engine/schemas/common/schemas'; + BulkActionEditType, +} from '../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { useBoolState } from '../../../../../../common/hooks/use_bool_state'; export const useBulkEditFormFlyout = () => { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.test.ts index cbdb34654a99b..361f7edc4823f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.test.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.test.ts @@ -8,7 +8,7 @@ import { BulkAction, BulkActionEditType, -} from '../../../../../../../../common/detection_engine/schemas/common/schemas'; +} from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { computeDryRunPayload } from './compute_dry_run_payload'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.ts index d50d6840e899a..c6128100985b0 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/utils/compute_dry_run_payload.ts @@ -5,11 +5,12 @@ * 2.0. */ -import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { BulkAction, BulkActionEditType, -} from '../../../../../../../../common/detection_engine/schemas/common/schemas'; +} from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { assertUnreachable } from '../../../../../../../../common/utility_types'; /** * helper utility that creates payload for _bulk_action API in dry mode @@ -53,5 +54,17 @@ export const computeDryRunPayload = ( value: { timeline_id: '', timeline_title: '' }, }, ]; + + case BulkActionEditType.add_rule_actions: + case BulkActionEditType.set_rule_actions: + return [ + { + type: editAction, + value: { throttle: '1h', actions: [] }, + }, + ]; + + default: + assertUnreachable(editAction); } }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_actions.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_actions.tsx index 8e32583a54c9b..cfd83c3ab408f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_actions.tsx @@ -9,7 +9,7 @@ import type { DefaultItemAction } from '@elastic/eui'; import { EuiToolTip } from '@elastic/eui'; import React from 'react'; import type { NavigateToAppOptions } from '@kbn/core/public'; -import { BulkAction } from '../../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkAction } from '../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { UseAppToasts } from '../../../../../common/hooks/use_app_toasts'; import { canEditRuleWithActions } from '../../../../../common/utils/privileges'; import type { Rule } from '../../../../containers/detection_engine/rules'; 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 86a54e099e7b2..278ed497d12e8 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 @@ -361,14 +361,44 @@ export const redirectToDetections = ( hasEncryptionKey === false || needsListsConfiguration; -const getRuleSpecificRuleParamKeys = (ruleType: Type) => { - const queryRuleParams = ['index', 'filters', 'language', 'query', 'saved_id']; +const commonRuleParamsKeys = [ + 'id', + 'name', + 'description', + 'false_positives', + 'rule_id', + 'max_signals', + 'risk_score', + 'output_index', + 'references', + 'severity', + 'timeline_id', + 'timeline_title', + 'threat', + 'type', + 'version', +]; +const queryRuleParams = ['index', 'filters', 'language', 'query', 'saved_id']; +const machineLearningRuleParams = ['anomaly_threshold', 'machine_learning_job_id']; +const thresholdRuleParams = ['threshold', ...queryRuleParams]; + +const getAllRuleParamsKeys = (): string[] => { + const allRuleParamsKeys = [ + ...commonRuleParamsKeys, + ...queryRuleParams, + ...machineLearningRuleParams, + ...thresholdRuleParams, + ].sort(); + return Array.from(new Set(allRuleParamsKeys)); +}; + +const getRuleSpecificRuleParamKeys = (ruleType: Type) => { switch (ruleType) { case 'machine_learning': - return ['anomaly_threshold', 'machine_learning_job_id']; + return machineLearningRuleParams; case 'threshold': - return ['threshold', ...queryRuleParams]; + return thresholdRuleParams; case 'new_terms': case 'threat_match': case 'query': @@ -380,24 +410,6 @@ const getRuleSpecificRuleParamKeys = (ruleType: Type) => { }; export const getActionMessageRuleParams = (ruleType: Type): string[] => { - const commonRuleParamsKeys = [ - 'id', - 'name', - 'description', - 'false_positives', - 'rule_id', - 'max_signals', - 'risk_score', - 'output_index', - 'references', - 'severity', - 'timeline_id', - 'timeline_title', - 'threat', - 'type', - 'version', - ]; - const ruleParamsKeys = [ ...commonRuleParamsKeys, ...getRuleSpecificRuleParamKeys(ruleType), @@ -406,12 +418,7 @@ export const getActionMessageRuleParams = (ruleType: Type): string[] => { return ruleParamsKeys; }; -export const getActionMessageParams = memoizeOne((ruleType: Type | undefined): ActionVariables => { - if (!ruleType) { - return { state: [], params: [] }; - } - const actionMessageRuleParams = getActionMessageRuleParams(ruleType); - // Prefixes are being added automatically by the ActionTypeForm +const transformRuleKeysToActionVariables = (actionMessageRuleParams: string[]): ActionVariables => { return { state: [{ name: 'signals_count', description: 'state.signals_count' }], params: [], @@ -428,8 +435,23 @@ export const getActionMessageParams = memoizeOne((ruleType: Type | undefined): A }), ], }; +}; + +export const getActionMessageParams = memoizeOne((ruleType: Type | undefined): ActionVariables => { + if (!ruleType) { + return { state: [], params: [] }; + } + const actionMessageRuleParams = getActionMessageRuleParams(ruleType); + + return transformRuleKeysToActionVariables(actionMessageRuleParams); }); +/** + * returns action variables available for all rule types + */ +export const getAllActionMessageParams = () => + transformRuleKeysToActionVariables(getAllRuleParamsKeys()); + // typed as null not undefined as the initial state for this value is null. export const userHasPermissions = (canUserCRUD: boolean | null): boolean => canUserCRUD != null ? canUserCRUD : true; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index c732c0a536b73..676356130bada 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -180,6 +180,13 @@ export const BULK_ACTION_APPLY_TIMELINE_TEMPLATE = i18n.translate( } ); +export const BULK_ACTION_ADD_RULE_ACTIONS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.addRuleActionsTitle', + { + defaultMessage: 'Add rule actions', + } +); + export const BULK_ACTION_MENU_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.contextMenuTitle', { diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx index 9f4e7b41a1c15..7c73cb4f24508 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx @@ -18,7 +18,7 @@ import { useHostRiskScore } from '../../../risk_score/containers'; export const HostsKpiComponent = React.memo( ({ filterQuery, from, indexNames, to, setQuery, skip, updateDateRange }) => { - const [_, { isModuleEnabled }] = useHostRiskScore({}); + const [_, { isModuleEnabled }] = useHostRiskScore(); return ( <> diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx index af0824501c032..67c9bb761be94 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx @@ -46,13 +46,18 @@ const HostRiskTabBodyComponent: React.FC< [startDate, endDate] ); + const filterQuery = useMemo( + () => (hostName ? buildHostNamesFilter([hostName]) : undefined), + [hostName] + ); + const { toggleStatus: overTimeToggleStatus, setToggleStatus: setOverTimeToggleStatus } = useQueryToggle(`${QUERY_ID} overTime`); const { toggleStatus: contributorsToggleStatus, setToggleStatus: setContributorsToggleStatus } = useQueryToggle(`${QUERY_ID} contributors`); const [loading, { data, refetch, inspect }] = useHostRiskScore({ - filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, + filterQuery, onlyLatest: false, skip: !overTimeToggleStatus && !contributorsToggleStatus, timerange, diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx index 6c57ea18a109f..48dea1e5d4b90 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx @@ -20,7 +20,7 @@ jest.mock('../../../risk_score/containers/all', () => ({ useHostRiskScore: jest.fn().mockReturnValue([ true, { - data: [], + data: undefined, isModuleEnabled: false, }, ]), @@ -72,7 +72,7 @@ describe('Host Summary Component', () => { ...mockProps, isInDetailsSidePanel: true, }; - const risk = 'very high hos risk'; + const risk = 'very high host risk'; const riskScore = 9999999; (useHostRiskScore as jest.Mock).mockReturnValue([ diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx index a914b059c3ba4..c6aad526117cd 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx @@ -78,8 +78,14 @@ export const HostOverview = React.memo( const capabilities = useMlCapabilities(); const userPermissions = hasMlUserPermissions(capabilities); const [darkMode] = useUiSetting$(DEFAULT_DARK_MODE); + const filterQuery = useMemo( + () => (hostName ? buildHostNamesFilter([hostName]) : undefined), + [hostName] + ); + const [_, { data: hostRisk, isModuleEnabled }] = useHostRiskScore({ - filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, + filterQuery, + skip: hostName == null, }); const getDefaultRenderer = useCallback( diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx index 5be86c8850029..5cf51615a395d 100644 --- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx @@ -20,7 +20,7 @@ jest.mock('../../../risk_score/containers/all', () => ({ useUserRiskScore: jest.fn().mockReturnValue([ true, { - data: [], + data: undefined, isModuleEnabled: false, }, ]), diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx index f13402171d543..6349a33a58fa3 100644 --- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx @@ -77,8 +77,13 @@ export const UserOverview = React.memo( const capabilities = useMlCapabilities(); const userPermissions = hasMlUserPermissions(capabilities); const [darkMode] = useUiSetting$(DEFAULT_DARK_MODE); + const filterQuery = useMemo( + () => (userName ? buildUserNamesFilter([userName]) : undefined), + [userName] + ); const [_, { data: userRisk, isModuleEnabled }] = useUserRiskScore({ - filterQuery: userName ? buildUserNamesFilter([userName]) : undefined, + filterQuery, + skip: userName == null, }); const getDefaultRenderer = useCallback( diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx index daa4c84e14212..2e3aa7c4d8d28 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx @@ -70,6 +70,13 @@ const OverviewComponent = () => { const riskyHostsEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); + const timerange = useMemo( + () => ({ + from, + to, + }), + [from, to] + ); return ( <> {indicesExist ? ( @@ -144,10 +151,7 @@ const OverviewComponent = () => { )} diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/all/index.test.tsx b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.test.tsx new file mode 100644 index 0000000000000..66fbb1d73bd74 --- /dev/null +++ b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.test.tsx @@ -0,0 +1,343 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { renderHook } from '@testing-library/react-hooks'; +import { useHostRiskScore, useUserRiskScore } from '.'; +import { TestProviders } from '../../../common/mock'; + +import { useSearchStrategy } from '../../../common/containers/use_search_strategy'; +import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; +import { useAppToasts } from '../../../common/hooks/use_app_toasts'; +import { useAppToastsMock } from '../../../common/hooks/use_app_toasts.mock'; + +jest.mock('../../../common/containers/use_search_strategy', () => ({ + useSearchStrategy: jest.fn(), +})); + +jest.mock('../../../common/hooks/use_space_id', () => ({ + useSpaceId: jest.fn().mockReturnValue('default'), +})); + +jest.mock('../../../common/hooks/use_experimental_features', () => ({ + useIsExperimentalFeatureEnabled: jest.fn(), +})); + +jest.mock('../../../common/hooks/use_app_toasts'); + +const mockUseSearchStrategy = useSearchStrategy as jest.Mock; +const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; +const mockSearch = jest.fn(); +const mockRefetch = jest.fn(); + +let appToastsMock: jest.Mocked>; + +describe('useHostRiskScore', () => { + beforeEach(() => { + jest.clearAllMocks(); + appToastsMock = useAppToastsMock.create(); + (useAppToasts as jest.Mock).mockReturnValue(appToastsMock); + }); + + test('does not search if feature is not enabled', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: undefined, + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: undefined, + }); + const { result } = renderHook(() => useHostRiskScore(), { + wrapper: TestProviders, + }); + expect(mockSearch).not.toHaveBeenCalled(); + expect(result.current).toEqual([ + false, + { + data: undefined, + inspect: {}, + isInspected: false, + isModuleEnabled: false, + refetch: mockRefetch, + totalCount: 0, + }, + ]); + }); + + test('handle index not found error', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: undefined, + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: { + attributes: { + caused_by: { + type: 'index_not_found_exception', + }, + }, + }, + }); + const { result } = renderHook(() => useHostRiskScore(), { + wrapper: TestProviders, + }); + expect(result.current).toEqual([ + false, + { + data: undefined, + inspect: {}, + isInspected: false, + isModuleEnabled: false, + refetch: mockRefetch, + totalCount: 0, + }, + ]); + }); + + test('show error toast', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + + const error = new Error(); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: undefined, + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error, + }); + renderHook(() => useHostRiskScore(), { + wrapper: TestProviders, + }); + expect(appToastsMock.addError).toHaveBeenCalledWith(error, { + title: 'Failed to run search on risk score', + }); + }); + + test('runs search if feature is enabled', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: [], + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: undefined, + }); + renderHook(() => useHostRiskScore(), { + wrapper: TestProviders, + }); + expect(mockSearch).toHaveBeenCalledWith({ + defaultIndex: ['ml_host_risk_score_latest_default'], + factoryQueryType: 'hostsRiskScore', + filterQuery: undefined, + pagination: undefined, + timerange: undefined, + sort: undefined, + }); + }); + + test('return result', async () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: [], + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: undefined, + }); + const { result, waitFor } = renderHook(() => useHostRiskScore(), { + wrapper: TestProviders, + }); + await waitFor(() => { + expect(result.current).toEqual([ + false, + { + data: [], + inspect: {}, + isInspected: false, + isModuleEnabled: true, + refetch: mockRefetch, + totalCount: 0, + }, + ]); + }); + }); +}); + +describe('useUserRiskScore', () => { + beforeEach(() => { + jest.clearAllMocks(); + appToastsMock = useAppToastsMock.create(); + (useAppToasts as jest.Mock).mockReturnValue(appToastsMock); + }); + + test('does not search if feature is not enabled', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: undefined, + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: undefined, + }); + const { result } = renderHook(() => useUserRiskScore(), { + wrapper: TestProviders, + }); + expect(mockSearch).not.toHaveBeenCalled(); + expect(result.current).toEqual([ + false, + { + data: undefined, + inspect: {}, + isInspected: false, + isModuleEnabled: false, + refetch: mockRefetch, + totalCount: 0, + }, + ]); + }); + + test('handle index not found error', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: undefined, + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: { + attributes: { + caused_by: { + type: 'index_not_found_exception', + }, + }, + }, + }); + const { result } = renderHook(() => useUserRiskScore(), { + wrapper: TestProviders, + }); + expect(result.current).toEqual([ + false, + { + data: undefined, + inspect: {}, + isInspected: false, + isModuleEnabled: false, + refetch: mockRefetch, + totalCount: 0, + }, + ]); + }); + + test('show error toast', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + + const error = new Error(); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: undefined, + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error, + }); + renderHook(() => useUserRiskScore(), { + wrapper: TestProviders, + }); + expect(appToastsMock.addError).toHaveBeenCalledWith(error, { + title: 'Failed to run search on risk score', + }); + }); + + test('runs search if feature is enabled', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: [], + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: undefined, + }); + renderHook(() => useUserRiskScore(), { + wrapper: TestProviders, + }); + expect(mockSearch).toHaveBeenCalledWith({ + defaultIndex: ['ml_user_risk_score_latest_default'], + factoryQueryType: 'usersRiskScore', + filterQuery: undefined, + pagination: undefined, + timerange: undefined, + sort: undefined, + }); + }); + + test('return result', async () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockUseSearchStrategy.mockReturnValue({ + loading: false, + result: { + data: [], + totalCount: 0, + }, + search: mockSearch, + refetch: mockRefetch, + inspect: {}, + error: undefined, + }); + const { result, waitFor } = renderHook(() => useUserRiskScore(), { + wrapper: TestProviders, + }); + await waitFor(() => { + expect(result.current).toEqual([ + false, + { + data: [], + inspect: {}, + isInspected: false, + isModuleEnabled: true, + refetch: mockRefetch, + totalCount: 0, + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx index cdafc7d63c8b1..ec1bf63ca3b39 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx +++ b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx @@ -5,21 +5,10 @@ * 2.0. */ -import deepEqual from 'fast-deep-equal'; -import { noop } from 'lodash/fp'; -import { useCallback, useEffect, useRef, useState } from 'react'; -import { Subscription } from 'rxjs'; +import { useEffect, useMemo } from 'react'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; import { createFilter } from '../../../common/containers/helpers'; -import { useKibana } from '../../../common/lib/kibana'; -import type { - RiskScoreStrategyResponse, - HostsRiskScore, - UsersRiskScore, - RiskScoreSortField, - RiskScoreRequestOptions, -} from '../../../../common/search_strategy'; +import type { RiskScoreSortField, StrategyResponseType } from '../../../../common/search_strategy'; import { getHostRiskIndex, RiskQueries, @@ -28,16 +17,16 @@ import { import type { ESQuery } from '../../../../common/typed_json'; import * as i18n from './translations'; -import { getInspectResponse } from '../../../helpers'; import type { InspectResponse } from '../../../types'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import { isIndexNotFoundError } from '../../../common/utils/exceptions'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import type { inputsModel } from '../../../common/store'; import { useSpaceId } from '../../../common/hooks/use_space_id'; +import { useSearchStrategy } from '../../../common/containers/use_search_strategy'; -export interface RiskScoreState { - data?: RiskScoreType; +export interface RiskScoreState { + data: undefined | StrategyResponseType['data']; inspect: InspectResponse; isInspected: boolean; refetch: inputsModel.Refetch; @@ -45,44 +34,40 @@ export interface RiskScoreState; - -type UseUserRiskScore = Omit; - -const isRecord = (item: unknown): item is Record => - typeof item === 'object' && !!item; +interface UseRiskScore extends UseRiskScoreParams { + defaultIndex: string | undefined; + factoryQueryType: T; + featureEnabled: boolean; +} -export const isRiskScoreHit = (item: unknown): item is HostsRiskScore | UsersRiskScore => - isRecord(item) && - (isRecord(item.host) || isRecord(item.user)) && - isRecord(item.risk_stats) && - typeof item.risk_stats?.risk_score === 'number' && - typeof item.risk === 'string'; +export const initialResult: Omit< + StrategyResponseType, + 'rawResponse' +> = { + totalCount: 0, + data: undefined, +}; -export const useHostRiskScore = ({ - timerange, - onlyLatest, - filterQuery, - sort, - skip = false, - pagination, -}: UseHostRiskScore): [boolean, RiskScoreState] => { +export const useHostRiskScore = (params?: UseRiskScoreParams) => { + const { timerange, onlyLatest, filterQuery, sort, skip = false, pagination } = params ?? {}; const spaceId = useSpaceId(); const defaultIndex = spaceId ? getHostRiskIndex(spaceId, onlyLatest) : undefined; - const riskyHostsFeatureEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); - return useRiskScore({ + return useRiskScore({ timerange, onlyLatest, filterQuery, @@ -91,22 +76,17 @@ export const useHostRiskScore = ({ pagination, featureEnabled: riskyHostsFeatureEnabled, defaultIndex, + factoryQueryType: RiskQueries.hostsRiskScore, }); }; -export const useUserRiskScore = ({ - timerange, - onlyLatest, - filterQuery, - sort, - skip = false, - pagination, -}: UseUserRiskScore): [boolean, RiskScoreState] => { +export const useUserRiskScore = (params?: UseRiskScoreParams) => { + const { timerange, onlyLatest, filterQuery, sort, skip = false, pagination } = params ?? {}; const spaceId = useSpaceId(); const defaultIndex = spaceId ? getUserRiskIndex(spaceId, onlyLatest) : undefined; const riskyUsersFeatureEnabled = useIsExperimentalFeatureEnabled('riskyUsersEnabled'); - return useRiskScore({ + return useRiskScore({ timerange, onlyLatest, filterQuery, @@ -115,155 +95,86 @@ export const useUserRiskScore = ({ pagination, featureEnabled: riskyUsersFeatureEnabled, defaultIndex, + factoryQueryType: RiskQueries.usersRiskScore, }); }; -export const useRiskScore = ({ +const useRiskScore = ({ timerange, - onlyLatest = true, filterQuery, sort, skip = false, pagination, featureEnabled, defaultIndex, -}: UseRiskScore): [boolean, RiskScoreState] => { + factoryQueryType, +}: UseRiskScore): [boolean, RiskScoreState] => { const { querySize, cursorStart } = pagination || {}; - const { data } = useKibana().services; - const refetch = useRef(noop); - const abortCtrl = useRef(new AbortController()); - const searchSubscription = useRef(new Subscription()); - const [loading, setLoading] = useState(featureEnabled); - const [riskScoreRequest, setRiskScoreRequest] = useState(null); - const { addError, addWarning } = useAppToasts(); - - const [riskScoreResponse, setRiskScoreResponse] = useState>({ - data: undefined, - inspect: { - dsl: [], - response: [], - }, - isInspected: false, - refetch: refetch.current, - totalCount: 0, - isModuleEnabled: undefined, + const { addError } = useAppToasts(); + + const { + loading, + result: response, + search, + refetch, + inspect, + error, + } = useSearchStrategy({ + factoryQueryType, + initialResult, + abort: skip, + showErrorToast: false, }); - const riskScoreSearch = useCallback( - (request: RiskScoreRequestOptions | null) => { - if (request == null || skip) { - return; - } - - const asyncSearch = async () => { - abortCtrl.current = new AbortController(); - setLoading(true); - - searchSubscription.current = data.search - .search(request, { - strategy: 'securitySolutionSearchStrategy', - abortSignal: abortCtrl.current.signal, - }) - .subscribe({ - next: (response) => { - if (isCompleteResponse(response)) { - const hits = response?.rawResponse?.hits?.hits; - - setRiskScoreResponse((prevResponse) => ({ - ...prevResponse, - data: isRiskScoreHit(hits?.[0]?._source) - ? (hits?.map((hit) => hit._source) as RiskScoreType) - : ([] as unknown as RiskScoreType), - inspect: getInspectResponse(response, prevResponse.inspect), - refetch: refetch.current, - totalCount: response.totalCount, - isModuleEnabled: true, - })); - searchSubscription.current.unsubscribe(); - setLoading(false); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_RISK_SCORE); - searchSubscription.current.unsubscribe(); - } - }, - error: (error) => { - setLoading(false); - if (isIndexNotFoundError(error)) { - setRiskScoreResponse((prevResponse) => - !prevResponse - ? prevResponse - : { - ...prevResponse, - isModuleEnabled: false, - } - ); - - setLoading(false); - } else { - addError(error, { title: i18n.FAIL_RISK_SCORE }); - } - - searchSubscription.current.unsubscribe(); - }, - }); - }; - searchSubscription.current.unsubscribe(); - abortCtrl.current.abort(); - if (featureEnabled) { - asyncSearch(); - } - - refetch.current = asyncSearch; - }, - [data.search, addError, addWarning, skip, featureEnabled] + const riskScoreResponse = useMemo( + () => ({ + data: response.data, + inspect, + refetch, + totalCount: response.totalCount, + isModuleEnabled: response.data != null, + isInspected: false, + }), + [inspect, refetch, response] ); - useEffect(() => { - if (defaultIndex) { - setRiskScoreRequest((prevRequest) => { - const myRequest = { - ...(prevRequest ?? {}), - defaultIndex: [defaultIndex], - factoryQueryType: RiskQueries.riskScore, - filterQuery: createFilter(filterQuery), - pagination: - cursorStart !== undefined && querySize !== undefined - ? { - cursorStart, - querySize, - } + const riskScoreRequest = useMemo( + () => + defaultIndex + ? { + defaultIndex: [defaultIndex], + factoryQueryType, + filterQuery: createFilter(filterQuery), + pagination: + cursorStart !== undefined && querySize !== undefined + ? { + cursorStart, + querySize, + } + : undefined, + timerange: timerange + ? { to: timerange.to, from: timerange.from, interval: '' } : undefined, - timerange: timerange - ? { to: timerange.to, from: timerange.from, interval: '' } - : undefined, - sort, - }; - - if (!deepEqual(prevRequest, myRequest)) { - return myRequest; - } - return prevRequest; - }); - } - }, [filterQuery, onlyLatest, timerange, cursorStart, querySize, sort, defaultIndex]); + sort, + } + : null, + [cursorStart, defaultIndex, factoryQueryType, filterQuery, querySize, sort, timerange] + ); useEffect(() => { - riskScoreSearch(riskScoreRequest); - return () => { - searchSubscription.current.unsubscribe(); - abortCtrl.current.abort(); - }; - }, [riskScoreRequest, riskScoreSearch]); + if (error) { + if (!isIndexNotFoundError(error)) { + addError(error, { title: i18n.FAIL_RISK_SCORE }); + } + } + }, [addError, error]); useEffect(() => { - if (skip) { - setLoading(false); - searchSubscription.current.unsubscribe(); - abortCtrl.current.abort(); + if (!skip && riskScoreRequest != null && featureEnabled) { + search(riskScoreRequest); } - }, [skip]); + }, [featureEnabled, riskScoreRequest, search, skip]); return [loading, riskScoreResponse]; }; diff --git a/x-pack/plugins/security_solution/public/risk_score/jest.config.js b/x-pack/plugins/security_solution/public/risk_score/jest.config.js new file mode 100644 index 0000000000000..d279b47287ffa --- /dev/null +++ b/x-pack/plugins/security_solution/public/risk_score/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['/x-pack/plugins/security_solution/public/risk_score'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/risk_score', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution/public/risk_score/**/*.{ts,tsx}', + ], + // See: https://github.com/elastic/kibana/issues/117255, the moduleNameMapper creates mocks to avoid memory leaks from kibana core. + moduleNameMapper: { + 'core/server$': '/x-pack/plugins/security_solution/server/__mocks__/core.mock.ts', + 'task_manager/server$': + '/x-pack/plugins/security_solution/server/__mocks__/task_manager.mock.ts', + 'alerting/server$': '/x-pack/plugins/security_solution/server/__mocks__/alert.mock.ts', + 'actions/server$': '/x-pack/plugins/security_solution/server/__mocks__/action.mock.ts', + }, +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/index.tsx index a6b38e0382e5f..22e4a8c568d19 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/index.tsx @@ -45,12 +45,21 @@ export const useToGetInternalFlyout = () => { const { alertId, isAlert, hostName, ruleName, timestamp } = useBasicDataFromDetailsData(detailsData); - const [hostRiskLoading, { data, isModuleEnabled }] = useHostRiskScore({ - filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, - pagination: { + const filterQuery = useMemo( + () => (hostName ? buildHostNamesFilter([hostName]) : undefined), + [hostName] + ); + + const pagination = useMemo( + () => ({ cursorStart: 0, querySize: 1, - }, + }), + [] + ); + const [hostRiskLoading, { data, isModuleEnabled }] = useHostRiskScore({ + filterQuery, + pagination, }); const hostRisk: HostRisk | null = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx index c9b1d42b535ec..7044ee548c3af 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx @@ -94,7 +94,17 @@ jest.mock( } ); jest.mock('../../../../detections/components/alerts_table/actions'); -const mockSearchStrategy = jest.fn(); +jest.mock('../../../../risk_score/containers', () => { + return { + useHostRiskScore: jest.fn().mockReturnValue([ + true, + { + data: undefined, + isModuleEnabled: false, + }, + ]), + }; +}); const defaultProps = { timelineId: TimelineId.test, @@ -121,16 +131,6 @@ describe('event details footer component', () => { (KibanaServices.get as jest.Mock).mockReturnValue(coreStartMock); (useKibana as jest.Mock).mockReturnValue({ services: { - data: { - search: { - searchStrategyClient: jest.fn(), - search: mockSearchStrategy.mockReturnValue({ - unsubscribe: jest.fn(), - subscribe: jest.fn(), - }), - }, - query: jest.fn(), - }, uiSettings: { get: jest.fn().mockReturnValue([]), }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 2c0247a4d61da..83e06db651e00 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -6,7 +6,7 @@ */ import { EuiSpacer } from '@elastic/eui'; -import React from 'react'; +import React, { useMemo } from 'react'; import deepEqual from 'fast-deep-equal'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -77,12 +77,22 @@ const EventDetailsPanelComponent: React.FC = ({ const { alertId, isAlert, hostName, ruleName, timestamp } = useBasicDataFromDetailsData(detailsData); - const [hostRiskLoading, { data, isModuleEnabled }] = useHostRiskScore({ - filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, - pagination: { + const filterQuery = useMemo( + () => (hostName ? buildHostNamesFilter([hostName]) : undefined), + [hostName] + ); + + const pagination = useMemo( + () => ({ cursorStart: 0, querySize: 1, - }, + }), + [] + ); + + const [hostRiskLoading, { data, isModuleEnabled }] = useHostRiskScore({ + filterQuery, + pagination, }); const hostRisk: HostRisk | null = data diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx index 4b32c1b6af078..89a45750c73b3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx @@ -144,6 +144,18 @@ describe('Details Panel Component', () => { mockState.timeline.timelineById[TimelineId.active].expandedDetail = eventExpandedDetail; mockState.timeline.timelineById.test.expandedDetail = eventExpandedDetail; store = createStore(mockState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + mockUseSearchStrategy.mockReturnValue({ + loading: true, + result: { + data: undefined, + totalCount: 0, + }, + error: undefined, + search: jest.fn(), + refetch: jest.fn(), + inspect: {}, + }); }); test('it should render the Event Details Panel when the panelView is set and the associated params are set', () => { diff --git a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx index 72e004c2d340d..3c97f0ad49b4e 100644 --- a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx @@ -19,7 +19,7 @@ import { RISKY_USERS_DOC_LINK } from '../constants'; export const UsersKpiComponent = React.memo( ({ filterQuery, from, indexNames, to, setQuery, skip, updateDateRange }) => { - const [_, { isModuleEnabled }] = useUserRiskScore({}); + const [_, { isModuleEnabled }] = useUserRiskScore(); return ( <> diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.tsx index 1f31e3c98992d..cef4740500a97 100644 --- a/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.tsx @@ -52,9 +52,12 @@ const UserRiskTabBodyComponent: React.FC< useQueryToggle(`${QUERY_ID} overTime`); const { toggleStatus: contributorsToggleStatus, setToggleStatus: setContributorsToggleStatus } = useQueryToggle(`${QUERY_ID} contributors`); - + const filterQuery = useMemo( + () => (userName ? buildUserNamesFilter([userName]) : undefined), + [userName] + ); const [loading, { data, refetch, inspect }] = useUserRiskScore({ - filterQuery: userName ? buildUserNamesFilter([userName]) : undefined, + filterQuery, onlyLatest: false, skip: !overTimeToggleStatus && !contributorsToggleStatus, timerange, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts index 3c288184e0736..56d93a1ed0336 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts @@ -21,10 +21,10 @@ import { MAX_RULES_TO_UPDATE_IN_PARALLEL, RULES_TABLE_MAX_PAGE_SIZE, } from '../../../../../common/constants'; -import { BulkAction } from '../../../../../common/detection_engine/schemas/common/schemas'; import { performBulkActionSchema, performBulkActionQuerySchema, + BulkAction, } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { SetupPlugins } from '../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../types'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index 9bc72854e0e22..1bb5ac4ac7c46 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -185,7 +185,10 @@ export const previewRulesRoute = async ( | 'getContext' | 'hasContext' >; - hasReachedAlertLimit: () => boolean; + alertLimit: { + getValue: () => number; + setLimitReached: () => void; + }; done: () => { getRecoveredAlerts: () => [] }; } ) => { @@ -287,7 +290,10 @@ export const previewRulesRoute = async ( () => true, { create: alertInstanceFactoryStub, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), } ); @@ -304,7 +310,10 @@ export const previewRulesRoute = async ( () => true, { create: alertInstanceFactoryStub, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), } ); @@ -321,7 +330,10 @@ export const previewRulesRoute = async ( () => true, { create: alertInstanceFactoryStub, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), } ); @@ -338,7 +350,10 @@ export const previewRulesRoute = async ( () => true, { create: alertInstanceFactoryStub, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), } ); @@ -353,7 +368,10 @@ export const previewRulesRoute = async ( () => true, { create: alertInstanceFactoryStub, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), } ); @@ -368,7 +386,10 @@ export const previewRulesRoute = async ( () => true, { create: alertInstanceFactoryStub, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), } ); @@ -383,7 +404,10 @@ export const previewRulesRoute = async ( () => true, { create: alertInstanceFactoryStub, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), } ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index 992db3ec1e89a..96ca9e9d5ef40 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -76,7 +76,10 @@ export const createRuleTypeMocks = ( scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), alertFactory: { create: jest.fn(() => ({ scheduleActions })), - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: jest.fn(() => 1000), + setLimitReached: jest.fn(() => {}), + }, done: jest.fn().mockResolvedValue({}), }, findAlerts: jest.fn(), // TODO: does this stay? diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts index abfdc5fe491a7..16db4e4955538 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts @@ -13,7 +13,7 @@ import type { EqlRuleParams } from '../../schemas/rule_schemas'; import { eqlRuleParams } from '../../schemas/rule_schemas'; import { eqlExecutor } from '../../signals/executors/eql'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; -import { validateImmutable, validateIndexPatterns } from '../utils'; +import { validateIndexPatterns } from '../utils'; export const createEqlAlertType = ( createOptions: CreateRuleOptions @@ -41,7 +41,6 @@ export const createEqlAlertType = ( * @returns mutatedRuleParams */ validateMutatedParams: (mutatedRuleParams) => { - validateImmutable(mutatedRuleParams.immutable); validateIndexPatterns(mutatedRuleParams.index); return mutatedRuleParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts index 94fc6d78965bb..6a370b381acd1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -13,7 +13,7 @@ import type { ThreatRuleParams } from '../../schemas/rule_schemas'; import { threatRuleParams } from '../../schemas/rule_schemas'; import { threatMatchExecutor } from '../../signals/executors/threat_match'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; -import { validateImmutable, validateIndexPatterns } from '../utils'; +import { validateIndexPatterns } from '../utils'; export const createIndicatorMatchAlertType = ( createOptions: CreateRuleOptions @@ -42,7 +42,6 @@ export const createIndicatorMatchAlertType = ( * @returns mutatedRuleParams */ validateMutatedParams: (mutatedRuleParams) => { - validateImmutable(mutatedRuleParams.immutable); validateIndexPatterns(mutatedRuleParams.index); return mutatedRuleParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts index 926615fc8d176..c24e9c5af8d4f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts @@ -13,7 +13,6 @@ import type { MachineLearningRuleParams } from '../../schemas/rule_schemas'; import { machineLearningRuleParams } from '../../schemas/rule_schemas'; import { mlExecutor } from '../../signals/executors/ml'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; -import { validateImmutable } from '../utils'; export const createMlAlertType = ( createOptions: CreateRuleOptions @@ -34,17 +33,6 @@ export const createMlAlertType = ( } return validated; }, - /** - * validate rule params when rule is bulk edited (update and created in future as well) - * returned params can be modified (useful in case of version increment) - * @param mutatedRuleParams - * @returns mutatedRuleParams - */ - validateMutatedParams: (mutatedRuleParams) => { - validateImmutable(mutatedRuleParams.immutable); - - return mutatedRuleParams; - }, }, }, actionGroups: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts index da37ddc49fce7..8a1f941a92f31 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts @@ -27,7 +27,7 @@ import { buildNewTermsAgg, } from './build_new_terms_aggregation'; import type { SignalSource } from '../../signals/types'; -import { validateImmutable, validateIndexPatterns } from '../utils'; +import { validateIndexPatterns } from '../utils'; import { parseDateString, validateHistoryWindowStart } from './utils'; import { addToSearchAfterReturn, createSearchAfterReturnType } from '../../signals/utils'; @@ -61,7 +61,6 @@ export const createNewTermsAlertType = ( * @returns mutatedRuleParams */ validateMutatedParams: (mutatedRuleParams) => { - validateImmutable(mutatedRuleParams.immutable); validateIndexPatterns(mutatedRuleParams.index); return mutatedRuleParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index 14e309a83c959..5a940ebe364c5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -13,7 +13,7 @@ import type { QueryRuleParams } from '../../schemas/rule_schemas'; import { queryRuleParams } from '../../schemas/rule_schemas'; import { queryExecutor } from '../../signals/executors/query'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; -import { validateImmutable, validateIndexPatterns } from '../utils'; +import { validateIndexPatterns } from '../utils'; export const createQueryAlertType = ( createOptions: CreateRuleOptions ): SecurityAlertType => { @@ -40,7 +40,6 @@ export const createQueryAlertType = ( * @returns mutatedRuleParams */ validateMutatedParams: (mutatedRuleParams) => { - validateImmutable(mutatedRuleParams.immutable); validateIndexPatterns(mutatedRuleParams.index); return mutatedRuleParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts index f8009220581e1..325d9adfb1bda 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts @@ -13,7 +13,7 @@ import type { CompleteRule, SavedQueryRuleParams } from '../../schemas/rule_sche import { savedQueryRuleParams } from '../../schemas/rule_schemas'; import { queryExecutor } from '../../signals/executors/query'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; -import { validateImmutable, validateIndexPatterns } from '../utils'; +import { validateIndexPatterns } from '../utils'; export const createSavedQueryAlertType = ( createOptions: CreateRuleOptions ): SecurityAlertType => { @@ -40,7 +40,6 @@ export const createSavedQueryAlertType = ( * @returns mutatedRuleParams */ validateMutatedParams: (mutatedRuleParams) => { - validateImmutable(mutatedRuleParams.immutable); validateIndexPatterns(mutatedRuleParams.index); return mutatedRuleParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts index cacc2f91a925f..1a419ddfd09c6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts @@ -14,7 +14,7 @@ import { thresholdRuleParams } from '../../schemas/rule_schemas'; import { thresholdExecutor } from '../../signals/executors/threshold'; import type { ThresholdAlertState } from '../../signals/types'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; -import { validateImmutable, validateIndexPatterns } from '../utils'; +import { validateIndexPatterns } from '../utils'; export const createThresholdAlertType = ( createOptions: CreateRuleOptions @@ -42,7 +42,6 @@ export const createThresholdAlertType = ( * @returns mutatedRuleParams */ validateMutatedParams: (mutatedRuleParams) => { - validateImmutable(mutatedRuleParams.immutable); validateIndexPatterns(mutatedRuleParams.index); return mutatedRuleParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/validate_mutated_params.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/validate_mutated_params.ts index 0aac2fc709588..f8a696f0c7f68 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/validate_mutated_params.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/validate_mutated_params.ts @@ -5,12 +5,6 @@ * 2.0. */ -export const validateImmutable = (immutable: boolean) => { - if (immutable === true) { - throw new Error("Elastic rule can't be edited"); - } -}; - export const validateIndexPatterns = (indices: string[] | undefined) => { if (indices?.length === 0) { throw new Error("Index patterns can't be empty"); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.test.ts index 8646a79d3d070..54da8ec923245 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.test.ts @@ -5,34 +5,40 @@ * 2.0. */ -import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { bulkEditActionToRulesClientOperation } from './action_to_rules_client_operation'; describe('bulkEditActionToRulesClientOperation', () => { - test('should transform tags bulk edit actions correctly f', () => { + test('should transform tags bulk edit actions correctly', () => { expect( bulkEditActionToRulesClientOperation({ type: BulkActionEditType.add_tags, value: ['test'] }) - ).toEqual({ - field: 'tags', - operation: 'add', - value: ['test'], - }); + ).toEqual([ + { + field: 'tags', + operation: 'add', + value: ['test'], + }, + ]); }); expect( bulkEditActionToRulesClientOperation({ type: BulkActionEditType.set_tags, value: ['test'] }) - ).toEqual({ - field: 'tags', - operation: 'set', - value: ['test'], - }); + ).toEqual([ + { + field: 'tags', + operation: 'set', + value: ['test'], + }, + ]); expect( bulkEditActionToRulesClientOperation({ type: BulkActionEditType.delete_tags, value: ['test'] }) - ).toEqual({ - field: 'tags', - operation: 'delete', - value: ['test'], - }); + ).toEqual([ + { + field: 'tags', + operation: 'delete', + value: ['test'], + }, + ]); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts index c75f7d0943e52..b462206aa8ff6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts @@ -7,10 +7,26 @@ import type { BulkEditOperation } from '@kbn/alerting-plugin/server'; -import type { BulkActionEditForRuleAttributes } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditForRuleAttributes } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { assertUnreachable } from '../../../../../common/utility_types'; +import { transformToAlertThrottle, transformToNotifyWhen } from '../utils'; + +const getThrottleOperation = (throttle: string) => + ({ + field: 'throttle', + operation: 'set', + value: transformToAlertThrottle(throttle), + } as const); + +const getNotifyWhenOperation = (throttle: string) => + ({ + field: 'notifyWhen', + operation: 'set', + value: transformToNotifyWhen(throttle), + } as const); + /** * converts bulk edit action to format of rulesClient.bulkEdit operation * @param action BulkActionEditForRuleAttributes @@ -18,31 +34,60 @@ import { assertUnreachable } from '../../../../../common/utility_types'; */ export const bulkEditActionToRulesClientOperation = ( action: BulkActionEditForRuleAttributes -): BulkEditOperation => { +): BulkEditOperation[] => { switch (action.type) { // tags actions case BulkActionEditType.add_tags: - return { - field: 'tags', - operation: 'add', - value: action.value, - }; + return [ + { + field: 'tags', + operation: 'add', + value: action.value, + }, + ]; case BulkActionEditType.delete_tags: - return { - field: 'tags', - operation: 'delete', - value: action.value, - }; + return [ + { + field: 'tags', + operation: 'delete', + value: action.value, + }, + ]; case BulkActionEditType.set_tags: - return { - field: 'tags', - operation: 'set', - value: action.value, - }; + return [ + { + field: 'tags', + operation: 'set', + value: action.value, + }, + ]; + + // rule actions + case BulkActionEditType.add_rule_actions: + return [ + { + field: 'actions', + operation: 'add', + value: action.value.actions, + }, + getThrottleOperation(action.value.throttle), + getNotifyWhenOperation(action.value.throttle), + ]; + + case BulkActionEditType.set_rule_actions: + return [ + { + field: 'actions', + operation: 'set', + value: action.value.actions, + }, + getThrottleOperation(action.value.throttle), + getNotifyWhenOperation(action.value.throttle), + ]; default: - return assertUnreachable(action.type); + return assertUnreachable(action); } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts index 592704842e6c2..8ea80e0fad430 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts @@ -6,7 +6,7 @@ */ import { addItemsToArray, deleteItemsFromArray, ruleParamsModifier } from './rule_params_modifier'; -import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { RuleAlertType } from '../types'; describe('addItemsToArray', () => { @@ -38,9 +38,13 @@ describe('deleteItemsFromArray', () => { }); describe('ruleParamsModifier', () => { - const ruleParamsMock = { index: ['my-index-*'], version: 1 } as RuleAlertType['params']; + const ruleParamsMock = { + index: ['initial-index-*'], + version: 1, + immutable: false, + } as RuleAlertType['params']; - test('should increment version', () => { + test('should increment version if rule is custom (immutable === false)', () => { const editedRuleParams = ruleParamsModifier(ruleParamsMock, [ { type: BulkActionEditType.add_index_patterns, @@ -50,6 +54,16 @@ describe('ruleParamsModifier', () => { expect(editedRuleParams).toHaveProperty('version', ruleParamsMock.version + 1); }); + test('should not increment version if rule is prebuilt (immutable === true)', () => { + const editedRuleParams = ruleParamsModifier({ ...ruleParamsMock, immutable: true }, [ + { + type: BulkActionEditType.add_index_patterns, + value: ['my-index-*'], + }, + ]); + expect(editedRuleParams).toHaveProperty('version', ruleParamsMock.version); + }); + describe('index_patterns', () => { describe('add_index_patterns action', () => { test.each([ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts index 2b3fb5de9ef79..14d49b14421b3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts @@ -7,8 +7,8 @@ import type { RuleAlertType } from '../types'; -import type { BulkActionEditForRuleParams } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditForRuleParams } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { invariant } from '../../../../../common/utils/invariant'; @@ -111,7 +111,10 @@ export const ruleParamsModifier = ( ); // increment version even if actions are empty, as attributes can be modified as well outside of ruleParamsModifier - modifiedParams.version += 1; + // version must not be modified for immutable rule. Otherwise prebuilt rules upgrade flow will be broken + if (existingRuleParams.immutable === false) { + modifiedParams.version += 1; + } return modifiedParams; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.test.ts index 0b7f540b4dcc3..46205c060be78 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { BulkActionEditPayload } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { splitBulkEditActions } from './split_bulk_edit_actions'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.ts index e1ba96390d538..f9f16a7684294 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/split_bulk_edit_actions.ts @@ -5,12 +5,12 @@ * 2.0. */ +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { BulkActionEditPayload, BulkActionEditForRuleAttributes, BulkActionEditForRuleParams, -} from '../../../../../common/detection_engine/schemas/common/schemas'; -import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/common/schemas'; +} from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; /** * Split bulk edit actions in 2 chunks: actions applied to params and @@ -32,6 +32,8 @@ export const splitBulkEditActions = (actions: BulkActionEditPayload[]) => { case BulkActionEditType.add_tags: case BulkActionEditType.set_tags: case BulkActionEditType.delete_tags: + case BulkActionEditType.add_rule_actions: + case BulkActionEditType.set_rule_actions: acc.attributesActions.push(action); break; default: diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/utils.ts index 65a4af2308e0e..91cfe9544d550 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/utils.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/common/schemas'; +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; /** * helper utility that defines whether bulk edit action is related to index patterns, i.e. one of: diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/validations.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/validations.ts index 28793f9fe9c88..5252fd1982ff3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/validations.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/validations.ts @@ -9,7 +9,8 @@ import type { Type as RuleType } from '@kbn/securitysolution-io-ts-alerting-type import { invariant } from '../../../../../common/utils/invariant'; import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { BulkActionsDryRunErrCode } from '../../../../../common/constants'; -import type { BulkActionEditPayload } from '../../../../../common/detection_engine/schemas/common/schemas'; +import type { BulkActionEditPayload } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { BulkActionEditType } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { RuleAlertType } from '../types'; import { isIndexPatternsBulkEditAction } from './utils'; import { throwDryRunError } from './dry_run'; @@ -24,6 +25,8 @@ interface BulkActionsValidationArgs { interface BulkEditBulkActionsValidationArgs { ruleType: RuleType; mlAuthz: MlAuthz; + edit: BulkActionEditPayload[]; + immutable: boolean; } interface DryRunBulkEditBulkActionsValidationArgs { @@ -78,8 +81,26 @@ export const validateBulkDuplicateRule = async ({ rule, mlAuthz }: BulkActionsVa export const validateBulkEditRule = async ({ ruleType, mlAuthz, + edit, + immutable, }: BulkEditBulkActionsValidationArgs) => { await throwMlAuthError(mlAuthz, ruleType); + + // if rule can't be edited error will be thrown + const canRuleBeEdited = !immutable || istEditApplicableToImmutableRule(edit); + await throwDryRunError( + () => invariant(canRuleBeEdited, "Elastic rule can't be edited"), + BulkActionsDryRunErrCode.IMMUTABLE + ); +}; + +/** + * add_rule_actions, set_rule_actions can be applied to prebuilt/immutable rules + */ +const istEditApplicableToImmutableRule = (edit: BulkActionEditPayload[]): boolean => { + return edit.every(({ type }) => + [BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].includes(type) + ); }; /** @@ -91,13 +112,12 @@ export const dryRunValidateBulkEditRule = async ({ edit, mlAuthz, }: DryRunBulkEditBulkActionsValidationArgs) => { - await validateBulkEditRule({ ruleType: rule.params.type, mlAuthz }); - - // if rule is immutable, it can't be edited - await throwDryRunError( - () => invariant(rule.params.immutable === false, "Elastic rule can't be edited"), - BulkActionsDryRunErrCode.IMMUTABLE - ); + await validateBulkEditRule({ + ruleType: rule.params.type, + mlAuthz, + edit, + immutable: rule.params.immutable, + }); // if rule is machine_learning, index pattern action can't be applied to it await throwDryRunError( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_edit_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_edit_rules.ts index 587d7d539caa6..b17b35fd010a8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_edit_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_edit_rules.ts @@ -5,15 +5,24 @@ * 2.0. */ -import type { RulesClient } from '@kbn/alerting-plugin/server'; -import type { BulkActionEditPayload } from '../../../../common/detection_engine/schemas/common'; +import pMap from 'p-map'; +import type { RulesClient, BulkEditError } from '@kbn/alerting-plugin/server'; +import type { + BulkActionEditPayload, + BulkActionEditPayloadRuleActions, +} from '../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { enrichFilterWithRuleTypeMapping } from './enrich_filter_with_rule_type_mappings'; import type { MlAuthz } from '../../machine_learning/authz'; - import { ruleParamsModifier } from './bulk_actions/rule_params_modifier'; import { splitBulkEditActions } from './bulk_actions/split_bulk_edit_actions'; import { validateBulkEditRule } from './bulk_actions/validations'; import { bulkEditActionToRulesClientOperation } from './bulk_actions/action_to_rules_client_operation'; +import { + NOTIFICATION_THROTTLE_NO_ACTIONS, + MAX_RULES_TO_UPDATE_IN_PARALLEL, +} from '../../../../common/constants'; +import { BulkActionEditType } from '../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { readRules } from './read_rules'; import type { RuleAlertType } from './types'; @@ -32,7 +41,7 @@ export interface BulkEditRulesArguments { * @param BulkEditRulesArguments * @returns edited rules and caught errors */ -export const bulkEditRules = ({ +export const bulkEditRules = async ({ rulesClient, ids, actions, @@ -41,12 +50,75 @@ export const bulkEditRules = ({ }: BulkEditRulesArguments) => { const { attributesActions, paramsActions } = splitBulkEditActions(actions); - return rulesClient.bulkEdit({ + const result = await rulesClient.bulkEdit({ ...(ids ? { ids } : { filter: enrichFilterWithRuleTypeMapping(filter) }), - operations: attributesActions.map(bulkEditActionToRulesClientOperation), + operations: attributesActions.map(bulkEditActionToRulesClientOperation).flat(), paramsModifier: async (ruleParams: RuleAlertType['params']) => { - await validateBulkEditRule({ mlAuthz, ruleType: ruleParams.type }); + await validateBulkEditRule({ + mlAuthz, + ruleType: ruleParams.type, + edit: actions, + immutable: ruleParams.immutable, + }); return ruleParamsModifier(ruleParams, paramsActions); }, }); + + // rulesClient bulkEdit currently doesn't support bulk mute/unmute. + // this is a workaround to mitigate this, + // until https://github.com/elastic/kibana/issues/139084 is resolved + // if rule actions has been applied: + // - we go through each rule + // - mute/unmute if needed, refetch rule + // calling mute for rule needed only when rule was unmuted before and throttle value is NOTIFICATION_THROTTLE_NO_ACTIONS + // calling unmute needed only if rule was muted and throttle value is not NOTIFICATION_THROTTLE_NO_ACTIONS + const ruleActions = attributesActions.filter((rule): rule is BulkActionEditPayloadRuleActions => + [BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].includes(rule.type) + ); + + // bulk edit actions are applying in a historical order. + // So, we need to find a rule action that will be applied the last, to be able to check if rule should be muted/unmuted + const rulesAction = ruleActions.pop(); + + if (rulesAction) { + const muteOrUnmuteErrors: BulkEditError[] = []; + const rulesToMuteOrUnmute = await pMap( + result.rules, + async (rule) => { + try { + if (rule.muteAll && rulesAction.value.throttle !== NOTIFICATION_THROTTLE_NO_ACTIONS) { + await rulesClient.unmuteAll({ id: rule.id }); + return (await readRules({ rulesClient, id: rule.id, ruleId: undefined })) ?? rule; + } else if ( + !rule.muteAll && + rulesAction.value.throttle === NOTIFICATION_THROTTLE_NO_ACTIONS + ) { + await rulesClient.muteAll({ id: rule.id }); + return (await readRules({ rulesClient, id: rule.id, ruleId: undefined })) ?? rule; + } + + return rule; + } catch (err) { + muteOrUnmuteErrors.push({ + message: err.message, + rule: { + id: rule.id, + name: rule.name, + }, + }); + + return null; + } + }, + { concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL } + ); + + return { + ...result, + rules: rulesToMuteOrUnmute.filter((rule): rule is RuleAlertType => rule != null), + errors: [...result.errors, ...muteOrUnmuteErrors], + }; + } + + return result; }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.ts index d010628e7fa22..0d2c01b735a5f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.ts @@ -9,7 +9,6 @@ import type { IEsSearchResponse } from '@kbn/data-plugin/common'; import type { SecuritySolutionFactory } from '../../types'; import type { RiskScoreRequestOptions, - RiskScoreStrategyResponse, RiskQueries, } from '../../../../../../common/search_strategy'; import { inspectStringifyObject } from '../../../../../utils/build_query'; @@ -17,7 +16,9 @@ import { buildRiskScoreQuery } from './query.risk_score.dsl'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { getTotalCount } from '../../cti/event_enrichment/helpers'; -export const riskScore: SecuritySolutionFactory = { +export const riskScore: SecuritySolutionFactory< + RiskQueries.hostsRiskScore | RiskQueries.usersRiskScore +> = { buildDsl: (options: RiskScoreRequestOptions) => { if (options.pagination && options.pagination.querySize >= DEFAULT_MAX_TABLE_QUERY_SIZE) { throw new Error(`No query size above ${DEFAULT_MAX_TABLE_QUERY_SIZE}`); @@ -25,20 +26,19 @@ export const riskScore: SecuritySolutionFactory = { return buildRiskScoreQuery(options); }, - parse: async ( - options: RiskScoreRequestOptions, - response: IEsSearchResponse - ): Promise => { + parse: async (options: RiskScoreRequestOptions, response: IEsSearchResponse) => { const inspect = { dsl: [inspectStringifyObject(buildRiskScoreQuery(options))], }; const totalCount = getTotalCount(response.rawResponse.hits.total); - + const hits = response?.rawResponse?.hits?.hits; + const data = hits?.map((hit) => hit._source) ?? []; return { ...response, inspect, totalCount, + data, }; }, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts index f6e36ad4ba29d..6d79b47ad51da 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts @@ -12,6 +12,7 @@ import { riskScore } from './all'; import { kpiRiskScore } from './kpi'; export const riskScoreFactory: Record> = { - [RiskQueries.riskScore]: riskScore, + [RiskQueries.hostsRiskScore]: riskScore, + [RiskQueries.usersRiskScore]: riskScore, [RiskQueries.kpiRiskScore]: kpiRiskScore, }; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts index 85b5adc8a2020..82dac9ba6ff9a 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts @@ -40,7 +40,10 @@ const alertFactory = (contextKeys: unknown[], testAlertActionArr: unknown[]) => ); return alertInstance; }, - hasReachedAlertLimit: () => false, + alertLimit: { + getValue: () => 1000, + setLimitReached: () => {}, + }, done: () => ({ getRecoveredAlerts: () => [] }), }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx index 46dba61339a7b..abbf73d9ad8c3 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/submit.tsx @@ -75,6 +75,7 @@ export const ActionBar = () => { ; + const result: RuleType = { + id: 'test.exceedsAlertLimit', + name: 'Test: ExceedsAlertLimit', + validate: { + params: paramsSchema, + }, + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + producer: 'alertsFixture', + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + async executor({ services, params, state }) { + let limit: number | null = null; + if (params.getsLimit) { + limit = services.alertFactory.alertLimit.getValue(); + } + + const alertsToCreate = limit ? limit : 25; + + range(alertsToCreate) + .map(() => uuid.v4()) + .forEach((id: string) => { + services.alertFactory.create(id).scheduleActions('default'); + }); + + if (params.reportsLimitReached) { + services.alertFactory.alertLimit.setLimitReached(true); + } + + // Index something + await services.scopedClusterClient.asCurrentUser.index({ + index: params.index, + refresh: 'wait_for', + body: { + numAlerts: alertsToCreate, + }, + }); + }, + }; + return result; +} + function getAuthorizationAlertType(core: CoreSetup) { const paramsSchema = schema.object({ callClusterAuthorizationIndex: schema.string(), @@ -786,4 +841,5 @@ export function defineAlertTypes( alerting.registerType(getLongRunningPatternRuleType(false)); alerting.registerType(getCancellableRuleType()); alerting.registerType(getPatternSuccessOrFailureAlertType()); + alerting.registerType(getExceedsAlertLimitRuleType()); } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts index 0a6394632b5a5..2f24648cff73f 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts @@ -72,6 +72,7 @@ export class FixturePlugin implements Plugin { + const objectRemover = new ObjectRemover(supertest); + + beforeEach(async () => { + await esTestIndexTool.destroy(); + await esTestIndexTool.setup(); + }); + + afterEach(async () => { + await objectRemover.removeAll(); + await esTestIndexTool.destroy(); + }); + + it('short circuits rule execution if rule type lets framework handle alert limit', async () => { + const ruleId = await createRule({ + name: 'no alert limit handling', + getsLimit: false, + reportsLimitReached: false, + }); + + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + }); + + // check that there's a warning in the execute event + const executeEvent = events[0]; + expect(executeEvent?.event?.outcome).to.eql('success'); + expect(executeEvent?.event?.reason).to.eql('maxAlerts'); + expect(executeEvent?.kibana?.alerting?.status).to.eql('warning'); + expect(executeEvent?.message).to.eql( + 'Rule reported more than the maximum number of alerts in a single run. Alerts may be missed and recovery notifications may be delayed' + ); + + // check there are no docs written out in the ES_TEST_INDEX + const results = await es.search( + { index: ES_TEST_INDEX_NAME, body: { query: { match_all: {} } } }, + { meta: true } + ); + // @ts-expect-error doesn't handle total: number + const value = results.body.hits.total.value?.value || results.body.hits.total.value; + expect(value).to.eql(0); + }); + + it('ends in error if rule type requests alert limit but does not report back whether it reached the limit', async () => { + const ruleId = await createRule({ + name: 'no alert limit handling', + getsLimit: true, + reportsLimitReached: false, + }); + + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + }); + + // check that there's an error in the execute event + const executeEvent = events[0]; + expect(executeEvent?.event?.outcome).to.eql('failure'); + expect(executeEvent?.event?.reason).to.eql('execute'); + expect(executeEvent?.kibana?.alerting?.status).to.eql('error'); + expect(executeEvent?.error?.message).to.eql( + `Rule has not reported whether alert limit has been reached after requesting limit value!` + ); + + // check there are docs written out in the ES_TEST_INDEX + const results = await es.search( + { index: ES_TEST_INDEX_NAME, body: { query: { match_all: {} } } }, + { meta: true } + ); + // @ts-expect-error doesn't handle total: number + const value = results.body.hits.total.value?.value || results.body.hits.total.value; + expect(value).to.eql(1); + + const hit = results.body.hits.hits[0]; + expect(hit._source).to.eql({ + numAlerts: 20, + }); + }); + + it('completes execution if rule type requests alert limit and reports back whether it reached the limit', async () => { + const ruleId = await createRule({ + name: 'no alert limit handling', + getsLimit: true, + reportsLimitReached: true, + }); + + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + }); + + // check that there's a warning in the execute event + const executeEvent = events[0]; + expect(executeEvent?.event?.outcome).to.eql('success'); + expect(executeEvent?.event?.reason).to.eql('maxAlerts'); + expect(executeEvent?.kibana?.alerting?.status).to.eql('warning'); + expect(executeEvent?.message).to.eql( + 'Rule reported more than the maximum number of alerts in a single run. Alerts may be missed and recovery notifications may be delayed' + ); + + // check there are docs written out in the ES_TEST_INDEX + const results = await es.search( + { index: ES_TEST_INDEX_NAME, body: { query: { match_all: {} } } }, + { meta: true } + ); + // @ts-expect-error doesn't handle total: number + const value = results.body.hits.total.value?.value || results.body.hits.total.value; + expect(value).to.eql(1); + + const hit = results.body.hits.hits[0]; + expect(hit._source).to.eql({ + numAlerts: 20, + }); + }); + + interface CreateRuleParams { + name: string; + getsLimit: boolean; + reportsLimitReached: boolean; + } + + async function createRule(params: CreateRuleParams): Promise { + const { status, body: createdRule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send({ + name: params.name, + consumer: 'alerts', + enabled: true, + rule_type_id: 'test.exceedsAlertLimit', + schedule: { interval: '1m' }, + actions: [], + notify_when: 'onActiveAlert', + params: { + index: ES_TEST_INDEX_NAME, + getsLimit: params.getsLimit, + reportsLimitReached: params.reportsLimitReached, + }, + }); + + expect(status).to.be(200); + + const ruleId = createdRule.id; + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + + return ruleId; + } + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/index.ts index ba5086785dc26..5f5e6f0e75c0c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/index.ts @@ -10,6 +10,14 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function alertingCircuitBreakerTests({ loadTestFile }: FtrProviderContext) { describe('circuit_breakers', () => { - loadTestFile(require.resolve('./max_alerts')); + /** + * This tests the expected behavior for a rule type that hits the alert limit in a single execution. + */ + loadTestFile(require.resolve('./alert_limit_services')); + /** + * This tests the expected behavior for the active and recovered alerts generated over + * a sequence of rule executions that hit the alert limit. + */ + loadTestFile(require.resolve('./index_threshold_max_alerts')); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/max_alerts.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/index_threshold_max_alerts.ts similarity index 99% rename from x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/max_alerts.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/index_threshold_max_alerts.ts index ae486e6b5b4ee..49935bbcaf24a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/max_alerts.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/circuit_breaker/index_threshold_max_alerts.ts @@ -29,7 +29,7 @@ export default function maxAlertsRuleTests({ getService }: FtrProviderContext) { const es = getService('es'); const esTestIndexTool = new ESTestIndexTool(es, retry); - describe('rule that hits max alerts circuit breaker', () => { + describe('index threshold rule that hits max alerts circuit breaker', () => { const objectRemover = new ObjectRemover(supertest); beforeEach(async () => { diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/update_rules_config.ts b/x-pack/test/api_integration/apis/cloud_security_posture/update_rules_config.ts index d48816200b3e8..67bd493f24885 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/update_rules_config.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/update_rules_config.ts @@ -37,7 +37,7 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); }); - it(`Should return 500 when package policy id does not exist`, async () => { + it('returns a 404 response when package policy id does not exist', async () => { const packagePolicyId = chance.guid(); const { body: response } = await supertest @@ -45,10 +45,11 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'xxxx') .send({ package_policy_id: packagePolicyId, + rules: [], }) - .expect(500); - expect(response.error).to.be('Internal Server Error'); - expect(response.message).to.be(`Package policy ${packagePolicyId} not found`); + .expect(404); + + expect(response.error).to.be('Not Found'); }); it(`Should return 200 for existing package policy id`, async () => { @@ -75,6 +76,7 @@ export default function ({ getService }: FtrProviderContext) { .post(`/internal/cloud_security_posture/update_rules_config`) .set('kbn-xsrf', 'xxxx') .send({ + rules: [], package_policy_id: postPackageResponse.item.id, }) .expect(200); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts index 4742eca71f9b0..4d4bda5e6b4e0 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts @@ -10,11 +10,14 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_BULK_ACTION, DETECTION_ENGINE_RULES_URL, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, } from '@kbn/security-solution-plugin/common/constants'; + import { BulkAction, BulkActionEditType, -} from '@kbn/security-solution-plugin/common/detection_engine/schemas/common/schemas'; +} from '@kbn/security-solution-plugin/common/detection_engine/schemas/request/perform_bulk_action_schema'; import { RulesSchema } from '@kbn/security-solution-plugin/common/detection_engine/schemas/response'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -30,6 +33,7 @@ import { getLegacyActionSO, installPrePackagedRules, getSimpleMlRule, + getWebHookAction, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -43,6 +47,31 @@ export default ({ getService }: FtrProviderContext): void => { const fetchRule = (ruleId: string) => supertest.get(`${DETECTION_ENGINE_RULES_URL}?rule_id=${ruleId}`).set('kbn-xsrf', 'true'); + const fetchPrebuiltRule = async () => { + const { body: findBody } = await supertest + .get( + `${DETECTION_ENGINE_RULES_URL}/_find?per_page=1&filter=alert.attributes.params.immutable: true` + ) + .set('kbn-xsrf', 'true'); + + return findBody.data[0]; + }; + + /** + * allows to get access to internal property: notifyWhen + */ + const fetchRuleByAlertApi = (ruleId: string) => + supertest.get(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'true'); + + const createWebHookAction = async () => + ( + await supertest + .post('/api/actions/action') + .set('kbn-xsrf', 'true') + .send(getWebHookAction()) + .expect(200) + ).body; + describe('perform_bulk_action', () => { beforeEach(async () => { await createSignalsIndex(supertest, log); @@ -905,24 +934,50 @@ export default ({ getService }: FtrProviderContext): void => { expect(rule.timeline_title).to.be(undefined); }); - it('should return error when trying to bulk edit immutable rule', async () => { - await installPrePackagedRules(supertest, log); - const { body: findBody } = await supertest - .get( - `${DETECTION_ENGINE_RULES_URL}/_find?per_page=1&filter=alert.attributes.params.immutable: true` - ) - .set('kbn-xsrf', 'true') - .send(); - const immutableRule = findBody.data[0]; + it('should return error if index patterns action is applied to machine learning rule', async () => { + const mlRule = await createRule(supertest, log, getSimpleMlRule()); const { body } = await postBulkAction() .send({ - ids: [immutableRule.id], + ids: [mlRule.id], action: BulkAction.edit, [BulkAction.edit]: [ { - type: BulkActionEditType.add_tags, - value: ['new-tag'], + type: BulkActionEditType.add_index_patterns, + value: ['index-*'], + }, + ], + }) + .expect(500); + + expect(body.attributes.summary).to.eql({ failed: 1, succeeded: 0, total: 1 }); + expect(body.attributes.errors[0]).to.eql({ + message: + "Index patterns can't be added. Machine learning rule doesn't have index patterns property", + status_code: 500, + rules: [ + { + id: mlRule.id, + name: mlRule.name, + }, + ], + }); + }); + + it('should return error if all index patterns removed from a rule', async () => { + const rule = await createRule(supertest, log, { + ...getSimpleRule(), + index: ['simple-index-*'], + }); + + const { body } = await postBulkAction() + .send({ + ids: [rule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.delete_index_patterns, + value: ['simple-index-*'], }, ], }) @@ -930,12 +985,12 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.attributes.summary).to.eql({ failed: 1, succeeded: 0, total: 1 }); expect(body.attributes.errors[0]).to.eql({ - message: "Mutated params invalid: Elastic rule can't be edited", + message: "Mutated params invalid: Index patterns can't be empty", status_code: 500, rules: [ { - id: immutableRule.id, - name: immutableRule.name, + id: rule.id, + name: rule.name, }, ], }); @@ -964,6 +1019,642 @@ export default ({ getService }: FtrProviderContext): void => { expect(updatedRule.version).to.be(rule.version + 1); }); + + describe('prebuilt rules', () => { + const cases = [ + { + type: BulkActionEditType.add_tags, + value: ['new-tag'], + }, + { + type: BulkActionEditType.set_tags, + value: ['new-tag'], + }, + { + type: BulkActionEditType.delete_tags, + value: ['new-tag'], + }, + { + type: BulkActionEditType.add_index_patterns, + value: ['test-*'], + }, + { + type: BulkActionEditType.set_index_patterns, + value: ['test-*'], + }, + { + type: BulkActionEditType.delete_index_patterns, + value: ['test-*'], + }, + { + type: BulkActionEditType.set_timeline, + value: { timeline_id: 'mock-id', timeline_title: 'mock-title' }, + }, + ]; + cases.forEach(({ type, value }) => { + it(`should return error when trying to apply "${type}" edit action to prebuilt rule`, async () => { + await installPrePackagedRules(supertest, log); + const prebuiltRule = await fetchPrebuiltRule(); + + const { body } = await postBulkAction() + .send({ + ids: [prebuiltRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type, + value, + }, + ], + }) + .expect(500); + + expect(body.attributes.summary).to.eql({ failed: 1, succeeded: 0, total: 1 }); + expect(body.attributes.errors[0]).to.eql({ + message: "Elastic rule can't be edited", + status_code: 500, + rules: [ + { + id: prebuiltRule.id, + name: prebuiltRule.name, + }, + ], + }); + }); + }); + }); + + describe('rule actions', () => { + const webHookActionMock = { + group: 'default', + params: { + body: '{}', + }, + }; + + describe('set_rule_actions', () => { + it('should set action correctly', async () => { + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, getSimpleRule(ruleId)); + + // create a new action + const hookAction = await createWebHookAction(); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_rule_actions, + value: { + throttle: '1h', + actions: [ + { + ...webHookActionMock, + id: hookAction.id, + }, + ], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].actions).to.eql([ + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + + // Check that the updates have been persisted + const { body: readRule } = await fetchRule(ruleId).expect(200); + + expect(readRule.actions).to.eql([ + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + }); + + it('should set actions to empty list, actions payload is empty list', async () => { + // create a new action + const hookAction = await createWebHookAction(); + + const defaultRuleAction = { + id: hookAction.id, + action_type_id: '.webhook', + group: 'default', + params: { + body: '{"test":"a default action"}', + }, + }; + + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, { + ...getSimpleRule(ruleId), + actions: [defaultRuleAction], + throttle: '1d', + }); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_rule_actions, + value: { + throttle: '1h', + actions: [], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].actions).to.eql([]); + + // Check that the updates have been persisted + const { body: readRule } = await fetchRule(ruleId).expect(200); + + expect(readRule.actions).to.eql([]); + }); + }); + + describe('add_rule_actions', () => { + it('should add action correctly to empty actions list', async () => { + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, getSimpleRule(ruleId)); + + // create a new action + const hookAction = await createWebHookAction(); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + throttle: '1h', + actions: [ + { + ...webHookActionMock, + id: hookAction.id, + }, + ], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].actions).to.eql([ + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + + // Check that the updates have been persisted + const { body: readRule } = await fetchRule(ruleId).expect(200); + + expect(readRule.actions).to.eql([ + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + }); + + it('should add action correctly to non empty actions list', async () => { + // create a new action + const hookAction = await createWebHookAction(); + + const defaultRuleAction = { + id: hookAction.id, + action_type_id: '.webhook', + group: 'default', + params: { + body: '{"test":"a default action"}', + }, + }; + + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, { + ...getSimpleRule(ruleId), + actions: [defaultRuleAction], + throttle: '1d', + }); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + throttle: '1h', + actions: [ + { + ...webHookActionMock, + id: hookAction.id, + }, + ], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].actions).to.eql([ + defaultRuleAction, + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + + // Check that the updates have been persisted + const { body: readRule } = await fetchRule(ruleId).expect(200); + + expect(readRule.actions).to.eql([ + defaultRuleAction, + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + }); + + it('should not change actions of rule if empty list of actions added', async () => { + // create a new action + const hookAction = await createWebHookAction(); + + const defaultRuleAction = { + id: hookAction.id, + action_type_id: '.webhook', + group: 'default', + params: { + body: '{"test":"a default action"}', + }, + }; + + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, { + ...getSimpleRule(ruleId), + actions: [defaultRuleAction], + throttle: '1d', + }); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + throttle: '1h', + actions: [], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].actions).to.eql([defaultRuleAction]); + + // Check that the updates have been persisted + const { body: readRule } = await fetchRule(ruleId).expect(200); + + expect(readRule.actions).to.eql([defaultRuleAction]); + }); + + it('should change throttle if actions list in payload is empty', async () => { + // create a new action + const hookAction = await createWebHookAction(); + + const defaultRuleAction = { + id: hookAction.id, + action_type_id: '.webhook', + group: 'default', + params: { + body: '{"test":"a default action"}', + }, + }; + + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, { + ...getSimpleRule(ruleId), + actions: [defaultRuleAction], + throttle: '8h', + }); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.add_rule_actions, + value: { + throttle: '1h', + actions: [], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].throttle).to.be('1h'); + + // Check that the updates have been persisted + const { body: readRule } = await fetchRule(ruleId).expect(200); + + expect(readRule.throttle).to.eql('1h'); + }); + }); + + describe('prebuilt rules', () => { + const cases = [ + { + type: BulkActionEditType.set_rule_actions, + }, + { + type: BulkActionEditType.add_rule_actions, + }, + ]; + cases.forEach(({ type }) => { + it(`should apply "${type}" rule action to prebuilt rule`, async () => { + await installPrePackagedRules(supertest, log); + const prebuiltRule = await fetchPrebuiltRule(); + const hookAction = await createWebHookAction(); + + const { body } = await postBulkAction() + .send({ + ids: [prebuiltRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type, + value: { + throttle: '1h', + actions: [ + { + ...webHookActionMock, + id: hookAction.id, + }, + ], + }, + }, + ], + }) + .expect(200); + + const editedRule = body.attributes.results.updated[0]; + // Check that the updated rule is returned with the response + expect(editedRule.actions).to.eql([ + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + // version of prebuilt rule should not change + expect(editedRule.version).to.be(prebuiltRule.version); + + // Check that the updates have been persisted + const { body: readRule } = await fetchRule(prebuiltRule.rule_id).expect(200); + + expect(readRule.actions).to.eql([ + { + ...webHookActionMock, + id: hookAction.id, + action_type_id: '.webhook', + }, + ]); + expect(prebuiltRule.version).to.be(readRule.version); + }); + }); + + // if rule action is applied together with another edit action, that can't be applied to prebuilt rule (for example: tags action) + // bulk edit request should return error + it(`should return error if one of edit action is not eligible for prebuilt rule`, async () => { + await installPrePackagedRules(supertest, log); + const prebuiltRule = await fetchPrebuiltRule(); + const hookAction = await createWebHookAction(); + + const { body } = await postBulkAction() + .send({ + ids: [prebuiltRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_rule_actions, + value: { + throttle: '1h', + actions: [ + { + ...webHookActionMock, + id: hookAction.id, + }, + ], + }, + }, + { + type: BulkActionEditType.set_tags, + value: ['tag-1'], + }, + ], + }) + .expect(500); + + expect(body.attributes.summary).to.eql({ failed: 1, succeeded: 0, total: 1 }); + expect(body.attributes.errors[0]).to.eql({ + message: "Elastic rule can't be edited", + status_code: 500, + rules: [ + { + id: prebuiltRule.id, + name: prebuiltRule.name, + }, + ], + }); + + // Check that the updates were not made + const { body: readRule } = await fetchRule(prebuiltRule.rule_id).expect(200); + + expect(readRule.actions).to.eql(prebuiltRule.actions); + expect(readRule.tags).to.eql(prebuiltRule.tags); + expect(readRule.version).to.be(prebuiltRule.version); + }); + }); + + describe('throttle', () => { + const casesForEmptyActions = [ + { + payloadThrottle: NOTIFICATION_THROTTLE_NO_ACTIONS, + }, + { + payloadThrottle: NOTIFICATION_THROTTLE_RULE, + }, + { + payloadThrottle: '1d', + }, + ]; + casesForEmptyActions.forEach(({ payloadThrottle }) => { + it(`throttle is set to NOTIFICATION_THROTTLE_NO_ACTIONS, if payload throttle="${payloadThrottle}" and actions list is empty`, async () => { + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, { + ...getSimpleRule(ruleId), + throttle: '8h', + }); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_rule_actions, + value: { + throttle: payloadThrottle, + actions: [], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].throttle).to.eql( + NOTIFICATION_THROTTLE_NO_ACTIONS + ); + + // Check that the updates have been persisted + const { body: rule } = await fetchRule(ruleId).expect(200); + + expect(rule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + }); + }); + + const casesForNonEmptyActions = [ + { + payloadThrottle: NOTIFICATION_THROTTLE_NO_ACTIONS, + expectedThrottle: NOTIFICATION_THROTTLE_NO_ACTIONS, + }, + { + payloadThrottle: NOTIFICATION_THROTTLE_RULE, + expectedThrottle: NOTIFICATION_THROTTLE_RULE, + }, + { + payloadThrottle: '1h', + expectedThrottle: '1h', + }, + ]; + casesForNonEmptyActions.forEach(({ payloadThrottle, expectedThrottle }) => { + it(`throttle is set correctly, if payload throttle="${payloadThrottle}" and actions non empty`, async () => { + // create a new action + const hookAction = await createWebHookAction(); + + const ruleId = 'ruleId'; + const createdRule = await createRule(supertest, log, getSimpleRule(ruleId)); + + const { body } = await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_rule_actions, + value: { + throttle: payloadThrottle, + actions: [ + { + id: hookAction.id, + group: 'default', + params: { body: '{}' }, + }, + ], + }, + }, + ], + }) + .expect(200); + + // Check that the updated rule is returned with the response + expect(body.attributes.results.updated[0].throttle).to.eql(expectedThrottle); + + // Check that the updates have been persisted + const { body: rule } = await fetchRule(ruleId).expect(200); + + expect(rule.throttle).to.eql(expectedThrottle); + }); + }); + }); + + describe('notifyWhen', () => { + const cases = [ + { + payload: { throttle: NOTIFICATION_THROTTLE_NO_ACTIONS }, + // keeps existing default value which is onActiveAlert + expected: { notifyWhen: 'onActiveAlert' }, + }, + { + payload: { throttle: '1d' }, + expected: { notifyWhen: 'onThrottleInterval' }, + }, + { + payload: { throttle: NOTIFICATION_THROTTLE_RULE }, + expected: { notifyWhen: 'onActiveAlert' }, + }, + ]; + cases.forEach(({ payload, expected }) => { + it(`should set notifyWhen correctly, if payload throttle="${payload.throttle}"`, async () => { + const createdRule = await createRule(supertest, log, getSimpleRule('ruleId')); + + await postBulkAction() + .send({ + ids: [createdRule.id], + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_rule_actions, + value: { + throttle: payload.throttle, + actions: [], + }, + }, + ], + }) + .expect(200); + + // Check whether notifyWhen set correctly + const { body: rule } = await fetchRuleByAlertApi(createdRule.id).expect(200); + + expect(rule.notify_when).to.eql(expected.notifyWhen); + }); + }); + }); + }); }); describe('overwrite_data_views', () => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action_dry_run.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action_dry_run.ts index 429b34f3f0a54..e9c3b3b68487d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action_dry_run.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action_dry_run.ts @@ -13,7 +13,7 @@ import { import { BulkAction, BulkActionEditType, -} from '@kbn/security-solution-plugin/common/detection_engine/schemas/common/schemas'; +} from '@kbn/security-solution-plugin/common/detection_engine/schemas/request/perform_bulk_action_schema'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createRule, diff --git a/x-pack/test/examples/search_examples/search_session_example.ts b/x-pack/test/examples/search_examples/search_session_example.ts index 373fc70bb7aa2..5f45b45743fe7 100644 --- a/x-pack/test/examples/search_examples/search_session_example.ts +++ b/x-pack/test/examples/search_examples/search_session_example.ts @@ -38,7 +38,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should start search, save session, restore session using "restore" button', async () => { await comboBox.setCustom('dataViewSelector', 'logstash-*'); await comboBox.setCustom('searchMetricField', 'bytes'); - await testSubjects.clickWhenNotDisabled('startSearch'); + await testSubjects.clickWhenNotDisabledWithoutRetry('startSearch'); await testSubjects.find('searchResults-1'); await searchSessions.expectState('completed'); await searchSessions.save(); diff --git a/x-pack/test/functional/apps/dashboard/group1/preserve_url.ts b/x-pack/test/functional/apps/dashboard/group1/preserve_url.ts index 3b6e07d98dc51..608d29a6b7abb 100644 --- a/x-pack/test/functional/apps/dashboard/group1/preserve_url.ts +++ b/x-pack/test/functional/apps/dashboard/group1/preserve_url.ts @@ -9,18 +9,29 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'dashboard', 'spaceSelector', 'header']); const appsMenu = getService('appsMenu'); const globalNav = getService('globalNav'); + const kibanaServer = getService('kibanaServer'); + const spacesService = getService('spaces'); describe('preserve url', function () { - before(async function () { - await esArchiver.load('x-pack/test/functional/es_archives/spaces/multi_space'); + const anotherSpace = 'another-space'; + + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_default_space' + ); + await spacesService.create({ id: anotherSpace, name: 'Another Space' }); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_another_space', + { space: anotherSpace } + ); }); - after(function () { - return esArchiver.unload('x-pack/test/functional/es_archives/spaces/multi_space'); + after(async () => { + await spacesService.delete(anotherSpace); + await kibanaServer.savedObjects.cleanStandardList(); }); it('goes back to last opened url', async function () { diff --git a/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_chart_action.ts b/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_chart_action.ts index 2e2c1f7ecca1d..6e91362e4adfd 100644 --- a/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_chart_action.ts +++ b/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_chart_action.ts @@ -47,7 +47,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('navigates to Discover app on action click carrying over pie slice filter', async () => { - await testSubjects.clickWhenNotDisabled(ACTION_TEST_SUBJ); + await testSubjects.clickWhenNotDisabledWithoutRetry(ACTION_TEST_SUBJ); await discover.waitForDiscoverAppOnScreen(); await filterBar.hasFilter('memory', '160,000 to 200,000'); const filterCount = await filterBar.getFilterCount(); @@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('navigates to Discover on click carrying over brushed time range', async () => { - await testSubjects.clickWhenNotDisabled(ACTION_TEST_SUBJ); + await testSubjects.clickWhenNotDisabledWithoutRetry(ACTION_TEST_SUBJ); await discover.waitForDiscoverAppOnScreen(); const newTimeRangeDurationHours = await timePicker.getTimeDurationInHours(); diff --git a/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts b/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts index 1dafddbb8567b..5028fa056ba76 100644 --- a/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts +++ b/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts @@ -64,7 +64,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('navigates to Discover app to index pattern of the panel on action click', async () => { - await testSubjects.clickWhenNotDisabled(ACTION_TEST_SUBJ); + await testSubjects.clickWhenNotDisabledWithoutRetry(ACTION_TEST_SUBJ); await discover.waitForDiscoverAppOnScreen(); const el = await testSubjects.find('discover-dataView-switch-link'); @@ -87,7 +87,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboard.saveDashboard('Dashboard with Pie Chart'); await panelActions.openContextMenu(); - await testSubjects.clickWhenNotDisabled(ACTION_TEST_SUBJ); + await testSubjects.clickWhenNotDisabledWithoutRetry(ACTION_TEST_SUBJ); await discover.waitForDiscoverAppOnScreen(); const text = await timePicker.getShowDatesButtonText(); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index c9a643c4c3048..66b870f42ade1 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -552,7 +552,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ); // check the JSON tab - await find.clickByCssSelectorWhenNotDisabled('#kbn_doc_viewer_tab_1'); + await find.clickByCssSelectorWhenNotDisabledWithoutRetry('#kbn_doc_viewer_tab_1'); await retry.waitForWithTimeout( 'index in flyout JSON tab is matching the logstash index', 5000, diff --git a/x-pack/test/functional/apps/discover/preserve_url.ts b/x-pack/test/functional/apps/discover/preserve_url.ts index aa11b99867371..278f1928335ec 100644 --- a/x-pack/test/functional/apps/discover/preserve_url.ts +++ b/x-pack/test/functional/apps/discover/preserve_url.ts @@ -9,17 +9,27 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'discover', 'spaceSelector', 'header']); const globalNav = getService('globalNav'); + const kibanaServer = getService('kibanaServer'); + const spacesService = getService('spaces'); describe('preserve url', function () { - before(async function () { - await esArchiver.load('x-pack/test/functional/es_archives/spaces/multi_space'); + const anotherSpace = 'another-space'; + + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_default_space' + ); + await spacesService.create({ id: anotherSpace, name: 'Another Space' }); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_another_space', + { space: anotherSpace } + ); }); - after(function () { - return esArchiver.unload('x-pack/test/functional/es_archives/spaces/multi_space'); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); }); it('goes back to last opened url', async function () { diff --git a/x-pack/test/functional/apps/lens/group2/show_underlying_data.ts b/x-pack/test/functional/apps/lens/group2/show_underlying_data.ts index bd8f02c723102..ab9b08104a8e7 100644 --- a/x-pack/test/functional/apps/lens/group2/show_underlying_data.ts +++ b/x-pack/test/functional/apps/lens/group2/show_underlying_data.ts @@ -35,7 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // expect the button is shown and enabled - await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`lnsApp_openInDiscover`); const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); await browser.switchToWindow(discoverWindowHandle); @@ -62,7 +62,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.waitForVisualization('xyVisChart'); // expect the button is shown and enabled - await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`lnsApp_openInDiscover`); const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); await browser.switchToWindow(discoverWindowHandle); @@ -98,7 +98,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.waitForVisualization('xyVisChart'); - await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`lnsApp_openInDiscover`); const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); await browser.switchToWindow(discoverWindowHandle); @@ -134,7 +134,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.waitForVisualization('xyVisChart'); // expect the button is shown and enabled - await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`lnsApp_openInDiscover`); const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); await browser.switchToWindow(discoverWindowHandle); @@ -169,7 +169,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.waitForVisualization('xyVisChart'); // expect the button is shown and enabled - await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`lnsApp_openInDiscover`); const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); await browser.switchToWindow(discoverWindowHandle); diff --git a/x-pack/test/functional/apps/visualize/preserve_url.ts b/x-pack/test/functional/apps/visualize/preserve_url.ts index 58e4d2ffcd4b2..a19459ebfd8b7 100644 --- a/x-pack/test/functional/apps/visualize/preserve_url.ts +++ b/x-pack/test/functional/apps/visualize/preserve_url.ts @@ -9,21 +9,32 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'visualize', 'spaceSelector', 'visChart']); const appsMenu = getService('appsMenu'); const globalNav = getService('globalNav'); + const kibanaServer = getService('kibanaServer'); + const spacesService = getService('spaces'); describe('preserve url', function () { - before(async function () { - await esArchiver.load('x-pack/test/functional/es_archives/spaces/multi_space'); + const anotherSpace = 'another-space'; + + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_default_space' + ); + await spacesService.create({ id: anotherSpace, name: 'Another Space' }); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_another_space', + { space: anotherSpace } + ); }); - after(function () { - return esArchiver.unload('x-pack/test/functional/es_archives/spaces/multi_space'); + after(async () => { + await spacesService.delete(anotherSpace); + await kibanaServer.savedObjects.cleanStandardList(); }); - it('goes back to last opened url', async function () { + it('goes back to last opened url', async () => { await PageObjects.common.navigateToApp('visualize'); await PageObjects.visualize.openSavedVisualization('A Pie'); await PageObjects.common.navigateToApp('home'); diff --git a/x-pack/test/functional/es_archives/spaces/multi_space/data.json b/x-pack/test/functional/es_archives/spaces/multi_space/data.json deleted file mode 100644 index 7da3f04eb1d97..0000000000000 --- a/x-pack/test/functional/es_archives/spaces/multi_space/data.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "type": "doc", - "value": { - "id": "config:6.0.0-alpha1", - "index": ".kibana", - "source": { - "config": { - "buildNum": 8467, - "dateFormat:tz": "UTC" - }, - "type": "config" - } - } -} - -{ - "type": "doc", - "value": { - "id": "space:default", - "index": ".kibana", - "source": { - "space": { - "description": "This is the default space!", - "name": "Default" - }, - "type": "space" - } - } -} - -{ - "type": "doc", - "value": { - "id": "space:another-space", - "index": ".kibana", - "source": { - "space": { - "description": "This is another space", - "name": "Another Space" - }, - "type": "space" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "type": "doc", - "id": "index-pattern:logstash-*", - "source": { - "index-pattern": { - "title": "logstash-*", - "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" - }, - "type": "index-pattern", - "migrationVersion": { - "index-pattern": "6.5.0" - }, - "updated_at": "2018-12-21T00:43:07.096Z" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "type": "doc", - "id": "visualization:75c3e060-1e7c-11e9-8488-65449e65d0ed", - "source": { - "visualization": { - "title": "A Pie", - "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } - }, - "type": "visualization", - "updated_at": "2019-01-22T19:32:31.206Z" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "type": "doc", - "id": "dashboard:my-dashboard", - "source": { - "dashboard": { - "title": "A Dashboard", - "hits": 0, - "description": "", - "panelsJSON": "[{\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"version\":\"7.0.0\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"id\":\"75c3e060-1e7c-11e9-8488-65449e65d0ed\",\"embeddableConfig\":{}}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "version": 1, - "timeRestore": false, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } - }, - "type": "dashboard", - "updated_at": "2019-01-22T19:32:47.232Z" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "type": "doc", - "id": "another-space:index-pattern:logstash-*", - "source": { - "index-pattern": { - "title": "logstash-*", - "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" - }, - "type": "index-pattern", - "migrationVersion": { - "index-pattern": "6.5.0" - }, - "namespace": "another-space", - "updated_at": "2018-12-21T00:43:07.096Z" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "type": "doc", - "id": "another-space:visualization:75c3e060-1e7c-11e9-8488-65449e65d0ed", - "source": { - "visualization": { - "title": "A Pie in another space", - "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } - }, - "namespace": "another-space", - "type": "visualization", - "updated_at": "2019-01-22T19:32:31.206Z" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "type": "doc", - "id": "another-space:dashboard:my-dashboard", - "source": { - "dashboard": { - "title": "A Dashboard in another space", - "hits": 0, - "description": "", - "panelsJSON": "[{\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"version\":\"7.0.0\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"id\":\"75c3e060-1e7c-11e9-8488-65449e65d0ed\",\"embeddableConfig\":{}}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "version": 1, - "timeRestore": false, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } - }, - "namespace": "another-space", - "type": "dashboard", - "updated_at": "2019-01-22T19:32:47.232Z" - } - } -} diff --git a/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json b/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json deleted file mode 100644 index 07dc66dd8ce94..0000000000000 --- a/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json +++ /dev/null @@ -1,245 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": {} - }, - "index": ".kibana_1", - "mappings": { - "properties": { - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - }, - "dateFormat:tz": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "dashboard": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "index-pattern": { - "dynamic": "strict", - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - } - } - }, - "search": { - "dynamic": "strict", - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "dynamic": "strict", - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "spaceId": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "url": { - "dynamic": "strict", - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchId": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} diff --git a/x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_another_space.json b/x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_another_space.json new file mode 100644 index 0000000000000..6d86e899364a7 --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_another_space.json @@ -0,0 +1,77 @@ +{ + "attributes": { + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "coreMigrationVersion": "8.5.0", + "id": "d1bd6c84-d9d0-56fb-8a72-63fe60020920", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "originId": "logstash-*", + "references": [], + "type": "index-pattern", + "updated_at": "2018-12-21T00:43:07.096Z", + "version": "WzE2LDJd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "A Pie in another space", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true,\"legendDisplay\":\"show\",\"legendSize\":\"auto\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}" + }, + "coreMigrationVersion": "8.5.0", + "id": "73f456f1-e0df-570b-82c1-0ff40e1321b9", + "migrationVersion": { + "visualization": "8.5.0" + }, + "originId": "75c3e060-1e7c-11e9-8488-65449e65d0ed", + "references": [ + { + "id": "d1bd6c84-d9d0-56fb-8a72-63fe60020920", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2019-01-22T19:32:31.206Z", + "version": "WzE4LDJd" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"}]", + "timeRestore": false, + "title": "A Dashboard in another space", + "version": 1 + }, + "coreMigrationVersion": "8.5.0", + "id": "09c616b0-b819-5f6e-b1a0-8c2d73a28e5e", + "migrationVersion": { + "dashboard": "8.5.0" + }, + "originId": "my-dashboard", + "references": [ + { + "id": "73f456f1-e0df-570b-82c1-0ff40e1321b9", + "name": "1:panel_1", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2019-01-22T19:32:47.232Z", + "version": "WzIwLDJd" +} \ No newline at end of file diff --git a/x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_default_space.json b/x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_default_space.json new file mode 100644 index 0000000000000..b92817b833122 --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/spaces/multi_space_default_space.json @@ -0,0 +1,74 @@ +{ + "attributes": { + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "coreMigrationVersion": "8.5.0", + "id": "logstash-*", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-12-21T00:43:07.096Z", + "version": "WzEzLDJd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "A Pie", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true,\"legendDisplay\":\"show\",\"legendSize\":\"auto\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}" + }, + "coreMigrationVersion": "8.5.0", + "id": "75c3e060-1e7c-11e9-8488-65449e65d0ed", + "migrationVersion": { + "visualization": "8.5.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2019-01-22T19:32:31.206Z", + "version": "WzE0LDJd" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"}]", + "timeRestore": false, + "title": "A Dashboard", + "version": 1 + }, + "coreMigrationVersion": "8.5.0", + "id": "my-dashboard", + "migrationVersion": { + "dashboard": "8.5.0" + }, + "references": [ + { + "id": "75c3e060-1e7c-11e9-8488-65449e65d0ed", + "name": "1:panel_1", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2019-01-22T19:32:47.232Z", + "version": "WzE1LDJd" +} \ No newline at end of file diff --git a/x-pack/test/functional/page_objects/gis_page.ts b/x-pack/test/functional/page_objects/gis_page.ts index 2e2f2706f7cb0..be0d8c9aaf07f 100644 --- a/x-pack/test/functional/page_objects/gis_page.ts +++ b/x-pack/test/functional/page_objects/gis_page.ts @@ -171,7 +171,7 @@ export class GisPageObject extends FtrService { } await this.testSubjects.click('savedObjectTitle'); } - await this.testSubjects.clickWhenNotDisabled('confirmSaveSavedObjectButton'); + await this.testSubjects.clickWhenNotDisabledWithoutRetry('confirmSaveSavedObjectButton'); await this.header.waitUntilLoadingHasFinished(); } diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 50f687654ce01..f45b8ad9c22da 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -1396,7 +1396,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont async typeFormula(formula: string) { await find.byCssSelector('.monaco-editor'); - await find.clickByCssSelectorWhenNotDisabled('.monaco-editor'); + await find.clickByCssSelectorWhenNotDisabledWithoutRetry('.monaco-editor'); const input = await find.activeElement(); await input.clearValueWithKeyboard({ charByChar: true }); await input.type(formula); diff --git a/x-pack/test/functional/services/aiops/explain_log_rate_spikes.ts b/x-pack/test/functional/services/aiops/explain_log_rate_spikes.ts index eab668f4e7fc7..646f39ed43e69 100644 --- a/x-pack/test/functional/services/aiops/explain_log_rate_spikes.ts +++ b/x-pack/test/functional/services/aiops/explain_log_rate_spikes.ts @@ -32,8 +32,10 @@ export function ExplainLogRateSpikesProvider({ getService }: FtrProviderContext) async clickUseFullDataButton(expectedFormattedTotalDocCount: string) { await retry.tryForTime(30 * 1000, async () => { - await testSubjects.clickWhenNotDisabled('aiopsExplainLogRatesSpikeButtonUseFullData'); - await testSubjects.clickWhenNotDisabled('superDatePickerApplyTimeButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry( + 'aiopsExplainLogRatesSpikeButtonUseFullData' + ); + await testSubjects.clickWhenNotDisabledWithoutRetry('superDatePickerApplyTimeButton'); await this.assertTotalDocumentCount(expectedFormattedTotalDocCount); }); }, @@ -72,7 +74,7 @@ export function ExplainLogRateSpikesProvider({ getService }: FtrProviderContext) }, async clickRerunAnalysisButton(shouldRerun: boolean) { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( `aiopsRerunAnalysisButton${shouldRerun ? ' shouldRerun' : ''}` ); diff --git a/x-pack/test/functional/services/dashboard/panel_drilldown_actions.ts b/x-pack/test/functional/services/dashboard/panel_drilldown_actions.ts index 13b0975fb002f..6b598c229d688 100644 --- a/x-pack/test/functional/services/dashboard/panel_drilldown_actions.ts +++ b/x-pack/test/functional/services/dashboard/panel_drilldown_actions.ts @@ -29,7 +29,7 @@ export function DashboardDrilldownPanelActionsProvider({ getService }: FtrProvid async clickCreateDrilldown() { log.debug('clickCreateDrilldown'); await this.expectExistsCreateDrilldownAction(); - await testSubjects.clickWhenNotDisabled(CREATE_DRILLDOWN_DATA_TEST_SUBJ); + await testSubjects.clickWhenNotDisabledWithoutRetry(CREATE_DRILLDOWN_DATA_TEST_SUBJ); } async expectExistsManageDrilldownsAction() { @@ -45,7 +45,7 @@ export function DashboardDrilldownPanelActionsProvider({ getService }: FtrProvid async clickManageDrilldowns() { log.debug('clickManageDrilldowns'); await this.expectExistsManageDrilldownsAction(); - await testSubjects.clickWhenNotDisabled(MANAGE_DRILLDOWNS_DATA_TEST_SUBJ); + await testSubjects.clickWhenNotDisabledWithoutRetry(MANAGE_DRILLDOWNS_DATA_TEST_SUBJ); } async expectMultipleActionsMenuOpened() { diff --git a/x-pack/test/functional/services/ml/anomaly_explorer.ts b/x-pack/test/functional/services/ml/anomaly_explorer.ts index 09583dbdfe766..8c2f8e8b09bb9 100644 --- a/x-pack/test/functional/services/ml/anomaly_explorer.ts +++ b/x-pack/test/functional/services/ml/anomaly_explorer.ts @@ -105,7 +105,7 @@ export function MachineLearningAnomalyExplorerProvider( async addAndEditSwimlaneInDashboard(dashboardTitle: string) { await retry.tryForTime(30 * 1000, async () => { await this.filterDashboardSearchWithSearchString(dashboardTitle); - await testSubjects.clickWhenNotDisabled('~mlEmbeddableAddAndEditDashboard'); + await testSubjects.clickWhenNotDisabledWithoutRetry('~mlEmbeddableAddAndEditDashboard'); // make sure the dashboard page actually loaded const dashboardItemCount = await dashboardPage.getSharedItemsCount(); diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index 8f0dbd0eed817..0d8ee7d11a8b1 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -164,7 +164,7 @@ export function MachineLearningCommonUIProvider({ }, async setMultiSelectFilter(testDataSubj: string, fieldTypes: string[]) { - await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`${testDataSubj}-button`); await testSubjects.existOrFail(`${testDataSubj}-popover`); await testSubjects.existOrFail(`${testDataSubj}-searchInput`); const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); @@ -186,7 +186,7 @@ export function MachineLearningCommonUIProvider({ }, async removeMultiSelectFilter(testDataSubj: string, fieldTypes: string[]) { - await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`${testDataSubj}-button`); await testSubjects.existOrFail(`${testDataSubj}-popover`); await testSubjects.existOrFail(`${testDataSubj}-searchInput`); const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); diff --git a/x-pack/test/functional/services/ml/dashboard_embeddables.ts b/x-pack/test/functional/services/ml/dashboard_embeddables.ts index 74268f74d19a2..d6328b6aa72b5 100644 --- a/x-pack/test/functional/services/ml/dashboard_embeddables.ts +++ b/x-pack/test/functional/services/ml/dashboard_embeddables.ts @@ -59,7 +59,7 @@ export function MachineLearningDashboardEmbeddablesProvider( const subj = 'mlAnomalyChartsInitializerConfirmButton'; await retry.tryForTime(60 * 1000, async () => { await this.assertInitializerConfirmButtonEnabled(); - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertAnomalyChartsEmbeddableInitializerNotExists(); }); }, diff --git a/x-pack/test/functional/services/ml/dashboard_job_selection_table.ts b/x-pack/test/functional/services/ml/dashboard_job_selection_table.ts index 4fc90585f1408..e571a92fd92c0 100644 --- a/x-pack/test/functional/services/ml/dashboard_job_selection_table.ts +++ b/x-pack/test/functional/services/ml/dashboard_job_selection_table.ts @@ -65,7 +65,7 @@ export function MachineLearningDashboardJobSelectionTableProvider({ const subj = this.rowSelector(jobId, `${jobId}-checkbox`); if ((await this.getRowCheckboxCheckedState(jobId)) !== expectCheckedState) { await retry.tryForTime(5 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertRowCheckboxCheckedState(jobId, expectCheckedState); }); } @@ -79,7 +79,7 @@ export function MachineLearningDashboardJobSelectionTableProvider({ async applyJobSelection() { const subj = 'mlFlyoutJobSelectorButtonApply'; - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertJobSelectionTableNotExists(); }, }; diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 77f1e34e67157..91ff6af4f5397 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -287,7 +287,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( const subj = 'mlDataFrameAnalyticsRuntimeMappingsEditorSwitch'; if ((await this.getRuntimeMappingsEditorSwitchCheckedState()) !== toggle) { await retry.tryForTime(5 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertRuntimeMappingsEditorSwitchCheckState(toggle); }); } @@ -316,7 +316,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async applyRuntimeMappings() { const subj = 'mlDataFrameAnalyticsRuntimeMappingsApplyButton'; await testSubjects.existOrFail(subj); - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); const isEnabled = await testSubjects.isEnabled(subj); expect(isEnabled).to.eql( false, @@ -466,7 +466,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async continueToAdditionalOptionsStep() { await retry.tryForTime(15 * 1000, async () => { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( 'mlAnalyticsCreateJobWizardConfigurationStep active > mlAnalyticsCreateJobWizardContinueButton' ); await this.assertAdditionalOptionsStepActive(); @@ -475,7 +475,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async continueToDetailsStep() { await retry.tryForTime(15 * 1000, async () => { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( 'mlAnalyticsCreateJobWizardAdvancedStep active > mlAnalyticsCreateJobWizardContinueButton' ); await this.assertDetailsStepActive(); @@ -484,7 +484,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async continueToValidationStep() { await retry.tryForTime(15 * 1000, async () => { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( 'mlAnalyticsCreateJobWizardDetailsStep active > mlAnalyticsCreateJobWizardContinueButton' ); await this.assertValidationStepActive(); @@ -504,7 +504,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async continueToCreateStep() { await retry.tryForTime(15 * 1000, async () => { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( 'mlAnalyticsCreateJobWizardValidationStepWrapper active > mlAnalyticsCreateJobWizardContinueButton' ); await this.assertCreateStepActive(); diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts index 1eeb43741af48..65531647d00ea 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts @@ -379,7 +379,7 @@ export function MachineLearningDataFrameAnalyticsResultsProvider( if (expandableContentExists !== shouldExpand) { await retry.tryForTime(5 * 1000, async () => { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( `mlDFExpandableSection-${sectionId}-toggle-button` ); if (shouldExpand) { diff --git a/x-pack/test/functional/services/ml/data_visualizer_file_based.ts b/x-pack/test/functional/services/ml/data_visualizer_file_based.ts index e793c9a76ad2c..0b8effd17cbb0 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_file_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_file_based.ts @@ -122,7 +122,7 @@ export function MachineLearningDataVisualizerFileBasedProvider( }, async startImportAndWaitForProcessing() { - await testSubjects.clickWhenNotDisabled('dataVisualizerFileImportButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('dataVisualizerFileImportButton'); await retry.tryForTime(60 * 1000, async () => { await testSubjects.existOrFail('dataVisualizerFileImportSuccessCallout'); }); diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 82ec33452a5b9..0e1860de4dab9 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -35,8 +35,8 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async clickUseFullDataButton(expectedFormattedTotalDocCount: string) { await retry.tryForTime(30 * 1000, async () => { - await testSubjects.clickWhenNotDisabled('dataVisualizerButtonUseFullData'); - await testSubjects.clickWhenNotDisabled('superDatePickerApplyTimeButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('dataVisualizerButtonUseFullData'); + await testSubjects.clickWhenNotDisabledWithoutRetry('superDatePickerApplyTimeButton'); await this.assertTotalDocumentCount(expectedFormattedTotalDocCount); }); }, @@ -154,7 +154,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ }, async clickCreateAdvancedJobButton() { - await testSubjects.clickWhenNotDisabled('dataVisualizerCreateAdvancedJobCard'); + await testSubjects.clickWhenNotDisabledWithoutRetry('dataVisualizerCreateAdvancedJobCard'); }, async assertCreateDataFrameAnalyticsCardExists() { @@ -183,7 +183,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async clickViewInDiscoverButton() { await retry.tryForTime(5000, async () => { - await testSubjects.clickWhenNotDisabled('dataVisualizerViewInDiscoverCard'); + await testSubjects.clickWhenNotDisabledWithoutRetry('dataVisualizerViewInDiscoverCard'); await PageObjects.discover.waitForDiscoverAppOnScreen(); }); }, diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_pattern_management.ts b/x-pack/test/functional/services/ml/data_visualizer_index_pattern_management.ts index 8ec908c41d3e7..eb00ac29e0527 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_pattern_management.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_pattern_management.ts @@ -37,7 +37,9 @@ export function MachineLearningDataVisualizerIndexPatternManagementProvider( async clickIndexPatternManagementButton() { await retry.tryForTime(5000, async () => { - await testSubjects.clickWhenNotDisabled('dataVisualizerDataViewManagementButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry( + 'dataVisualizerDataViewManagementButton' + ); await this.assertIndexPatternManagementMenuExists(); }); }, @@ -45,7 +47,7 @@ export function MachineLearningDataVisualizerIndexPatternManagementProvider( async clickAddIndexPatternFieldAction() { await retry.tryForTime(5000, async () => { await this.assertIndexPatternManagementMenuExists(); - await testSubjects.clickWhenNotDisabled('dataVisualizerAddDataViewFieldAction'); + await testSubjects.clickWhenNotDisabledWithoutRetry('dataVisualizerAddDataViewFieldAction'); await this.assertIndexPatternFieldEditorExists(); }); }, @@ -53,7 +55,7 @@ export function MachineLearningDataVisualizerIndexPatternManagementProvider( async clickManageIndexPatternAction() { await retry.tryForTime(5000, async () => { await this.assertIndexPatternManagementMenuExists(); - await testSubjects.clickWhenNotDisabled('dataVisualizerManageDataViewAction'); + await testSubjects.clickWhenNotDisabledWithoutRetry('dataVisualizerManageDataViewAction'); await testSubjects.existOrFail('editIndexPattern'); }); }, diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index 04d9bf98c7b04..ace274f5cabf7 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -300,7 +300,7 @@ export function MachineLearningDataVisualizerTableProvider( docCountFormatted: string ) { await this.assertSampleSizeInputExists(); - await testSubjects.clickWhenNotDisabled('dataVisualizerShardSizeSelect'); + await testSubjects.clickWhenNotDisabledWithoutRetry('dataVisualizerShardSizeSelect'); await testSubjects.existOrFail(`dataVisualizerShardSizeOption ${sampleSize}`); await testSubjects.click(`dataVisualizerShardSizeOption ${sampleSize}`); @@ -567,7 +567,7 @@ export function MachineLearningDataVisualizerTableProvider( public async assertLensActionShowChart(fieldName: string, visualizationContainer?: string) { await retry.tryForTime(30 * 1000, async () => { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( this.rowSelector(fieldName, 'dataVisualizerActionViewInLensButton') ); await testSubjects.existOrFail(visualizationContainer ?? 'lnsVisualizationContainer', { diff --git a/x-pack/test/functional/services/ml/job_annotations_table.ts b/x-pack/test/functional/services/ml/job_annotations_table.ts index 90b47e9f8b455..89824378e7619 100644 --- a/x-pack/test/functional/services/ml/job_annotations_table.ts +++ b/x-pack/test/functional/services/ml/job_annotations_table.ts @@ -191,7 +191,7 @@ export function MachineLearningJobAnnotationsProvider({ getService }: FtrProvide public async clickAnnotationsEditAction(annotationId: string) { await this.assertAnnotationsEditActionExists(annotationId); await retry.tryForTime(1000, async () => { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( this.rowSelector(annotationId, 'mlAnnotationsActionEdit') ); await testSubjects.existOrFail('mlAnnotationFlyout'); @@ -273,9 +273,9 @@ export function MachineLearningJobAnnotationsProvider({ getService }: FtrProvide await this.clickAnnotationsEditAction(annotationId); await testSubjects.existOrFail('mlAnnotationFlyout'); await testSubjects.existOrFail('mlAnnotationsFlyoutDeleteButton'); - await testSubjects.clickWhenNotDisabled('mlAnnotationsFlyoutDeleteButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlAnnotationsFlyoutDeleteButton'); await testSubjects.existOrFail('mlAnnotationFlyoutConfirmDeleteModal'); - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( '~mlAnnotationFlyoutConfirmDeleteModal > ~confirmModalConfirmButton' ); }); @@ -372,7 +372,7 @@ export function MachineLearningJobAnnotationsProvider({ getService }: FtrProvide await this.ensureAnnotationsActionsMenuOpen(annotationId); await this.assertAnnotationsDelayedDataChartActionExists(); - await testSubjects.clickWhenNotDisabled('mlAnnotationsActionViewDatafeed'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlAnnotationsActionViewDatafeed'); await testSubjects.existOrFail('mlAnnotationsViewDatafeedFlyout'); await testSubjects.existOrFail('mlAnnotationsViewDatafeedFlyoutTitle'); diff --git a/x-pack/test/functional/services/ml/job_management.ts b/x-pack/test/functional/services/ml/job_management.ts index c07cc91921fd7..9828a55777545 100644 --- a/x-pack/test/functional/services/ml/job_management.ts +++ b/x-pack/test/functional/services/ml/job_management.ts @@ -19,7 +19,7 @@ export function MachineLearningJobManagementProvider( return { async navigateToNewJobSourceSelection() { - await testSubjects.clickWhenNotDisabled('mlCreateNewJobButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlCreateNewJobButton'); await testSubjects.existOrFail('mlPageSourceSelection'); }, diff --git a/x-pack/test/functional/services/ml/job_source_selection.ts b/x-pack/test/functional/services/ml/job_source_selection.ts index 9fa51b01516a5..95a2bf0dbab79 100644 --- a/x-pack/test/functional/services/ml/job_source_selection.ts +++ b/x-pack/test/functional/services/ml/job_source_selection.ts @@ -26,7 +26,7 @@ export function MachineLearningJobSourceSelectionProvider({ getService }: FtrPro async selectSource(sourceName: string, nextPageSubj: string) { await this.filterSourceSelection(sourceName); await retry.tryForTime(30 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(`savedObjectTitle${sourceName}`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`savedObjectTitle${sourceName}`); await testSubjects.existOrFail(nextPageSubj, { timeout: 10 * 1000 }); }); }, diff --git a/x-pack/test/functional/services/ml/job_type_selection.ts b/x-pack/test/functional/services/ml/job_type_selection.ts index e2ec5217c7f8c..b17ecf3ab138a 100644 --- a/x-pack/test/functional/services/ml/job_type_selection.ts +++ b/x-pack/test/functional/services/ml/job_type_selection.ts @@ -12,7 +12,7 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi return { async selectSingleMetricJob() { - await testSubjects.clickWhenNotDisabled('mlJobTypeLinkSingleMetricJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobTypeLinkSingleMetricJob'); await this.assertSingleMetricJobWizardOpen(); }, @@ -21,7 +21,7 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi }, async selectMultiMetricJob() { - await testSubjects.clickWhenNotDisabled('mlJobTypeLinkMultiMetricJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobTypeLinkMultiMetricJob'); await this.assertMultiMetricJobWizardOpen(); }, @@ -30,7 +30,7 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi }, async selectPopulationJob() { - await testSubjects.clickWhenNotDisabled('mlJobTypeLinkPopulationJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobTypeLinkPopulationJob'); await this.assertPopulationJobWizardOpen(); }, @@ -39,7 +39,7 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi }, async selectAdvancedJob() { - await testSubjects.clickWhenNotDisabled('mlJobTypeLinkAdvancedJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobTypeLinkAdvancedJob'); await this.assertAdvancedJobWizardOpen(); }, @@ -48,7 +48,7 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi }, async selectCategorizationJob() { - await testSubjects.clickWhenNotDisabled('mlJobTypeLinkCategorizationJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobTypeLinkCategorizationJob'); await this.assertCategorizationJobWizardOpen(); }, diff --git a/x-pack/test/functional/services/ml/job_wizard_advanced.ts b/x-pack/test/functional/services/ml/job_wizard_advanced.ts index f9924e373e89e..ca36cb3687c18 100644 --- a/x-pack/test/functional/services/ml/job_wizard_advanced.ts +++ b/x-pack/test/functional/services/ml/job_wizard_advanced.ts @@ -314,12 +314,12 @@ export function MachineLearningJobWizardAdvancedProvider( }, async confirmAddDetectorModal() { - await testSubjects.clickWhenNotDisabled('mlCreateDetectorModalSaveButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlCreateDetectorModalSaveButton'); await testSubjects.missingOrFail('mlCreateDetectorModal'); }, async cancelAddDetectorModal() { - await testSubjects.clickWhenNotDisabled('mlCreateDetectorModalCancelButton'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlCreateDetectorModalCancelButton'); await testSubjects.missingOrFail('mlCreateDetectorModal'); }, @@ -359,7 +359,7 @@ export function MachineLearningJobWizardAdvancedProvider( }, async createJob() { - await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobWizardButtonCreateJob'); await testSubjects.existOrFail('mlStartDatafeedModal', { timeout: 10 * 1000 }); }, }; diff --git a/x-pack/test/functional/services/ml/job_wizard_categorization.ts b/x-pack/test/functional/services/ml/job_wizard_categorization.ts index 508a2ff4297af..997720cc45d23 100644 --- a/x-pack/test/functional/services/ml/job_wizard_categorization.ts +++ b/x-pack/test/functional/services/ml/job_wizard_categorization.ts @@ -23,7 +23,7 @@ export function MachineLearningJobWizardCategorizationProvider({ getService }: F async selectCategorizationDetectorType(identifier: string) { const id = `~mlJobWizardCategorizationDetector${identifier}Card`; await testSubjects.existOrFail(id); - await testSubjects.clickWhenNotDisabled(id); + await testSubjects.clickWhenNotDisabledWithoutRetry(id); await testSubjects.existOrFail(`mlJobWizardCategorizationDetector${identifier}Card selected`); }, diff --git a/x-pack/test/functional/services/ml/job_wizard_common.ts b/x-pack/test/functional/services/ml/job_wizard_common.ts index 7df09652f8953..86375122a4a1a 100644 --- a/x-pack/test/functional/services/ml/job_wizard_common.ts +++ b/x-pack/test/functional/services/ml/job_wizard_common.ts @@ -32,7 +32,7 @@ export function MachineLearningJobWizardCommonProvider( return { async clickNextButton() { await testSubjects.existOrFail('mlJobWizardNavButtonNext'); - await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonNext'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobWizardNavButtonNext'); }, async assertTimeRangeSectionExists() { @@ -329,7 +329,7 @@ export function MachineLearningJobWizardCommonProvider( })) === false ) { await retry.tryForTime(5 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertDedicatedIndexSwitchCheckedState(true, { withAdvancedSection: sectionOptions.withAdvancedSection, }); @@ -362,7 +362,7 @@ export function MachineLearningJobWizardCommonProvider( const subj = 'mlJobWizardStartDatafeedCheckbox'; if ((await this.getStartDatafeedSwitchCheckedState()) !== toggle) { await retry.tryForTime(5 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertStartDatafeedSwitchCheckedState(toggle); }); } @@ -492,7 +492,7 @@ export function MachineLearningJobWizardCommonProvider( }, async clickUseFullDataButton(expectedStartDate: string, expectedEndDate: string) { - await testSubjects.clickWhenNotDisabled('mlButtonUseFullData'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlButtonUseFullData'); await this.assertDateRangeSelection(expectedStartDate, expectedEndDate); }, @@ -540,12 +540,12 @@ export function MachineLearningJobWizardCommonProvider( }, async createJobAndWaitForCompletion() { - await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobWizardButtonCreateJob'); await testSubjects.existOrFail('mlJobWizardButtonRunInRealTime', { timeout: 2 * 60 * 1000 }); }, async createJobWithoutDatafeedStart() { - await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob'); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobWizardButtonCreateJob'); await testSubjects.existOrFail('mlPageJobManagement'); }, }; diff --git a/x-pack/test/functional/services/ml/lens_visualizations.ts b/x-pack/test/functional/services/ml/lens_visualizations.ts index 1c8a4410f57a7..a6b69d0174817 100644 --- a/x-pack/test/functional/services/ml/lens_visualizations.ts +++ b/x-pack/test/functional/services/ml/lens_visualizations.ts @@ -20,7 +20,9 @@ export function MachineLearningLensVisualizationsProvider( await testSubjects.click('embeddablePanelAction-create-ml-ad-job-action'); }, async clickCreateJob(layerIndex: number) { - await testSubjects.clickWhenNotDisabled(`mlLensLayerCreateJobButton_${layerIndex}`); + await testSubjects.clickWhenNotDisabledWithoutRetry( + `mlLensLayerCreateJobButton_${layerIndex}` + ); }, async clickCreateJobFromLayerWithWizard(layerIndex: number) { await testSubjects.click(`mlLensLayerCreateWithWizardButton_${layerIndex}`); diff --git a/x-pack/test/functional/services/ml/settings_calendar.ts b/x-pack/test/functional/services/ml/settings_calendar.ts index 8959e93623c1c..617deeaa0634e 100644 --- a/x-pack/test/functional/services/ml/settings_calendar.ts +++ b/x-pack/test/functional/services/ml/settings_calendar.ts @@ -284,7 +284,7 @@ export function MachineLearningSettingsCalendarProvider( const subj = 'mlCalendarApplyToAllJobsSwitch'; if ((await this.getApplyToAllJobsSwitchCheckedState()) !== toggle) { await retry.tryForTime(5 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertApplyToAllJobsSwitchCheckState(toggle); }); } diff --git a/x-pack/test/functional/services/ml/stack_management_jobs.ts b/x-pack/test/functional/services/ml/stack_management_jobs.ts index f9d5dbea3d621..9f37003be2175 100644 --- a/x-pack/test/functional/services/ml/stack_management_jobs.ts +++ b/x-pack/test/functional/services/ml/stack_management_jobs.ts @@ -84,7 +84,9 @@ export function MachineLearningStackManagementJobsProvider({ }, async executeSync() { - await testSubjects.clickWhenNotDisabled('mlJobMgmtSyncFlyoutSyncButton', { timeout: 5000 }); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobMgmtSyncFlyoutSyncButton', { + timeout: 5000, + }); // check and close success toast const resultToast = await toasts.getToastElement(1); @@ -136,7 +138,7 @@ export function MachineLearningStackManagementJobsProvider({ }, async saveAndCloseSpacesFlyout() { - await testSubjects.clickWhenNotDisabled('sts-save-button', { timeout: 2000 }); + await testSubjects.clickWhenNotDisabledWithoutRetry('sts-save-button', { timeout: 2000 }); await testSubjects.missingOrFail('share-to-space-flyout', { timeout: 2000 }); }, @@ -276,7 +278,9 @@ export function MachineLearningStackManagementJobsProvider({ }, async importJobs() { - await testSubjects.clickWhenNotDisabled('mlJobMgmtImportImportButton', { timeout: 5000 }); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobMgmtImportImportButton', { + timeout: 5000, + }); // check and close success toast const resultToast = await toasts.getToastElement(1); @@ -342,7 +346,9 @@ export function MachineLearningStackManagementJobsProvider({ }, async selectExportJobs() { - await testSubjects.clickWhenNotDisabled('mlJobMgmtExportExportButton', { timeout: 5000 }); + await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobMgmtExportExportButton', { + timeout: 5000, + }); // check and close success toast const resultToast = await toasts.getToastElement(1); diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts index c8d43207dd5ab..03b0d961e1d4c 100644 --- a/x-pack/test/functional/services/ml/trained_models_table.ts +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -284,7 +284,7 @@ export function TrainedModelsTableProvider( } public async openStartDeploymentModal(modelId: string) { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( this.rowSelector(modelId, 'mlModelsTableRowStartDeploymentAction'), { timeout: 5000 } ); @@ -292,7 +292,7 @@ export function TrainedModelsTableProvider( } public async clickStopDeploymentAction(modelId: string) { - await testSubjects.clickWhenNotDisabled( + await testSubjects.clickWhenNotDisabledWithoutRetry( this.rowSelector(modelId, 'mlModelsTableRowStopDeploymentAction'), { timeout: 5000 } ); diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index 3cdd667ecba78..6491c7a8b0595 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -55,6 +55,15 @@ export function ObservabilityAlertsCommonProvider({ ); }; + const navigateToAlertDetails = async (alertId: string) => { + return await pageObjects.common.navigateToUrlWithBrowserHistory( + 'observability', + `/alerts/${alertId}`, + '', + { ensureCurrentUrl: false } + ); + }; + const navigateToRuleDetailsByRuleId = async (ruleId: string) => { return await pageObjects.common.navigateToUrlWithBrowserHistory( 'observability', @@ -146,7 +155,7 @@ export function ObservabilityAlertsCommonProvider({ const getViewAlertDetailsFlyoutButton = async () => { await openActionsMenuForRow(0); - return await testSubjects.find('viewAlertDetails'); + return await testSubjects.find('viewAlertDetailsFlyout'); }; const openAlertsFlyout = async () => { @@ -335,5 +344,6 @@ export function ObservabilityAlertsCommonProvider({ getRuleStatValue, navigateToRulesPage, navigateToRuleDetailsByRuleId, + navigateToAlertDetails, }; } diff --git a/x-pack/test/functional/services/transform/source_selection.ts b/x-pack/test/functional/services/transform/source_selection.ts index b5096a2dd2f3d..9f77dbeb7ba40 100644 --- a/x-pack/test/functional/services/transform/source_selection.ts +++ b/x-pack/test/functional/services/transform/source_selection.ts @@ -26,7 +26,7 @@ export function TransformSourceSelectionProvider({ getService }: FtrProviderCont async selectSource(sourceName: string) { await this.filterSourceSelection(sourceName); await retry.tryForTime(30 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(`savedObjectTitle${sourceName}`); + await testSubjects.clickWhenNotDisabledWithoutRetry(`savedObjectTitle${sourceName}`); await testSubjects.existOrFail('transformPageCreateTransform', { timeout: 10 * 1000 }); }); }, diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index df65911cb4098..9eff6dff611e3 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -34,7 +34,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi return { async clickNextButton() { await testSubjects.existOrFail('transformWizardNavButtonNext'); - await testSubjects.clickWhenNotDisabled('transformWizardNavButtonNext'); + await testSubjects.clickWhenNotDisabledWithoutRetry('transformWizardNavButtonNext'); }, async assertDefineStepActive() { @@ -317,7 +317,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi const subj = 'transformAdvancedRuntimeMappingsEditorSwitch'; if ((await this.getRuntimeMappingsEditorSwitchCheckedState()) !== toggle) { await retry.tryForTime(5 * 1000, async () => { - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); await this.assertRuntimeMappingsEditorSwitchCheckState(toggle); }); } @@ -355,7 +355,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi async applyRuntimeMappings() { const subj = 'transformRuntimeMappingsApplyButton'; await testSubjects.existOrFail(subj); - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabledWithoutRetry(subj); const isEnabled = await testSubjects.isEnabled(subj); expect(isEnabled).to.eql( false, @@ -560,7 +560,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi break; } } - await testSubjects.clickWhenNotDisabled('transformApplyAggChanges'); + await testSubjects.clickWhenNotDisabledWithoutRetry('transformApplyAggChanges'); await testSubjects.missingOrFail(`transformAggPopoverForm_${expectedLabel}`); }, diff --git a/x-pack/test/observability_functional/apps/observability/index.ts b/x-pack/test/observability_functional/apps/observability/index.ts index 1fbe8f5f975e5..b3acbf5f51a8a 100644 --- a/x-pack/test/observability_functional/apps/observability/index.ts +++ b/x-pack/test/observability_functional/apps/observability/index.ts @@ -21,5 +21,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./pages/rules_page')); loadTestFile(require.resolve('./pages/rule_details_page')); + loadTestFile(require.resolve('./pages/alert_details_page')); }); } diff --git a/x-pack/test/observability_functional/apps/observability/pages/alert_details_page.ts b/x-pack/test/observability_functional/apps/observability/pages/alert_details_page.ts new file mode 100644 index 0000000000000..472af7376d02e --- /dev/null +++ b/x-pack/test/observability_functional/apps/observability/pages/alert_details_page.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 uuid from 'uuid'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const testSubjects = getService('testSubjects'); + const esArchiver = getService('esArchiver'); + const observability = getService('observability'); + const retry = getService('retry'); + + describe('Observability Alert Details page - Feature flag', function () { + this.tags('includeFirefox'); + + before(async () => { + await observability.alerts.common.setKibanaTimeZoneToUTC(); + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + }); + + it('should show 404 page when the feature flag is disabled', async () => { + await observability.alerts.common.navigateToAlertDetails(uuid.v4()); + await retry.waitFor( + 'Alerts page to be visible', + async () => await testSubjects.exists('pageNotFound') + ); + }); + // This test is will be removed after removing the feature flag. + // FLAKY for the same reason: https://github.com/elastic/kibana/issues/133799 + describe.skip('Alert Detail / Alert Flyout', () => { + before(async () => { + await observability.alerts.common.navigateToTimeWithData(); + }); + it('should open the flyout instead of the alerts details page when clicking on "View alert details" from the... (3 dots) button when the feature flag is disabled', async () => { + await observability.alerts.common.openAlertsFlyout(); + await observability.alerts.common.getAlertsFlyoutOrFail(); + }); + /* TODO: Add more test cases regarding the feature flag for: + - alert details URL from the Action variable + - alert details button from the alert flyout. + */ + }); + }); +}; diff --git a/x-pack/test/observability_functional/apps/observability/pages/rule_details_page.ts b/x-pack/test/observability_functional/apps/observability/pages/rule_details_page.ts index d451de95fa9f8..6d17b9c6e0920 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/rule_details_page.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/rule_details_page.ts @@ -145,6 +145,9 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await observability.alerts.common.navigateToRuleDetailsByRuleId(logThresholdRuleId); }); + after(async () => { + await observability.users.restoreDefaultTestUserRole(); + }); it('should show the actions button if user has permissions', async () => { await retry.waitFor( 'Actions button to be visible', diff --git a/x-pack/test/osquery_cypress/artifact_manager.ts b/x-pack/test/osquery_cypress/artifact_manager.ts index 5bc80dc9aa710..bef18d0177220 100644 --- a/x-pack/test/osquery_cypress/artifact_manager.ts +++ b/x-pack/test/osquery_cypress/artifact_manager.ts @@ -5,11 +5,6 @@ * 2.0. */ -// import axios from 'axios'; -// import { last } from 'lodash'; - export async function getLatestVersion(): Promise { - return '8.4.0-SNAPSHOT'; - // const response: any = await axios('https://artifacts-api.elastic.co/v1/versions'); - // return last(response.data.versions as string[]) || '8.2.0-SNAPSHOT'; + return '8.5.0-SNAPSHOT'; } diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts index 5fcbd6f2cb998..63f40f46023b8 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts @@ -126,6 +126,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsResults.expandClassificationEvaluationSection(false); await ml.dataFrameAnalyticsResults.expandFeatureImportanceSection(false); await ml.dataFrameAnalyticsResults.expandScatterplotMatrixSection(false); + await ml.dataFrameAnalyticsResults.enableResultsTablePreviewHistogramCharts(true); await ml.dataFrameAnalyticsResults.scrollAnalysisIntoView(); await commonScreenshots.removeFocusFromElement(); await commonScreenshots.takeScreenshot( @@ -156,7 +157,7 @@ export default function ({ getService }: FtrProviderContext) { 'confusion-matrix-binary-accuracy', screenshotDirectories ); - await ml.dataFrameAnalyticsResults.scrollRocCurveChartIntoView(); + await ml.dataFrameAnalyticsResults.scrollClassificationEvaluationIntoView(); await commonScreenshots.takeScreenshot( 'flights-classification-roc-curve', screenshotDirectories diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index 055213b0b2e2f..cb6ffd7d79f32 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -52,7 +52,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { 'riskyHostsEnabled', 'riskyUsersEnabled', 'entityAnalyticsDashboardEnabled', - 'insightsRelatedAlertsByProcessAncestry', 'threatIntelligenceEnabled', ])}`, `--home.disableWelcomeScreen=true`, diff --git a/yarn.lock b/yarn.lock index 3592d046af317..dd04af67f4bb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1478,10 +1478,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@47.2.1": - version "47.2.1" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-47.2.1.tgz#a3fcd0d4dbccd7528d62a55dbbe8d11050d2b191" - integrity sha512-OeEfr0eO758BzS/IjSxW/O0cu9AvmpQ/dmy15E1Wz5hRTi+0hgHkW/vSOKju5/UHB6ulX36uRmHAJ1x5zuQ3MQ== +"@elastic/charts@48.0.0": + version "48.0.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-48.0.0.tgz#ccbbe7507102f078b80285e045a421105c0fc780" + integrity sha512-sj0L0JKU3KLJw0Ci1RzSj5WkhGc7ptAft/ulunF+w0i5vG7qgDdbnemCvRIPkgGiu2AnAnIj/bkK3IcxpAbmGA== dependencies: "@popperjs/core" "^2.4.0" bezier-easing "^2.1.0" @@ -1502,7 +1502,7 @@ resize-observer-polyfill "^1.5.1" ts-debounce "^4.0.0" utility-types "^3.10.0" - uuid "^3.3.2" + uuid "^8.3.2" "@elastic/datemath@5.0.3": version "5.0.3" @@ -3139,6 +3139,14 @@ version "0.0.0" uid "" +"@kbn/core-rendering-browser-internal@link:bazel-bin/packages/core/rendering/core-rendering-browser-internal": + version "0.0.0" + uid "" + +"@kbn/core-rendering-browser-mocks@link:bazel-bin/packages/core/rendering/core-rendering-browser-mocks": + version "0.0.0" + uid "" + "@kbn/core-saved-objects-api-browser@link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser": version "0.0.0" uid "" @@ -7229,6 +7237,14 @@ version "0.0.0" uid "" +"@types/kbn__core-rendering-browser-internal@link:bazel-bin/packages/core/rendering/core-rendering-browser-internal/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__core-rendering-browser-mocks@link:bazel-bin/packages/core/rendering/core-rendering-browser-mocks/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__core-saved-objects-api-browser@link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser/npm_module_types": version "0.0.0" uid "" @@ -13816,7 +13832,7 @@ ejs@^3.1.6, ejs@^3.1.8: dependencies: jake "^10.8.5" -elastic-apm-http-client@11.0.1: +elastic-apm-http-client@11.0.1, elastic-apm-http-client@^11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.0.1.tgz#15dbe99d56d62b3f732d1bd2b51bef6094b78801" integrity sha512-5AOWlhs2WlZpI+DfgGqY/8Rk7KF8WeevaO8R961eBylavU6GWhLRNiJncohn5jsvrqhmeT19azBvy/oYRN7bJw==