diff --git a/.buildkite/disabled_jest_configs.json b/.buildkite/disabled_jest_configs.json index a64c34ae741b4..fe51488c7066f 100644 --- a/.buildkite/disabled_jest_configs.json +++ b/.buildkite/disabled_jest_configs.json @@ -1,3 +1 @@ -[ - "x-pack/plugins/watcher/jest.config.js" -] +[] diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 70be71f08184a..e6eb1cec755c8 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -34,11 +34,8 @@ disabled: - x-pack/test/osquery_cypress/cli_config.ts - x-pack/test/osquery_cypress/config.ts - x-pack/test/osquery_cypress/visual_config.ts - - x-pack/test/security_solution_cypress/ccs_config.ts - x-pack/test/security_solution_cypress/cli_config.ts - - x-pack/test/security_solution_cypress/config.firefox.ts - x-pack/test/security_solution_cypress/config.ts - - x-pack/test/security_solution_cypress/upgrade_config.ts - x-pack/test/threat_intelligence_cypress/cli_config_parallel.ts - x-pack/test/threat_intelligence_cypress/config.ts - x-pack/test/functional_enterprise_search/visual_config.ts @@ -328,6 +325,7 @@ enabled: - x-pack/test/functional/config_security_basic.ts - x-pack/test/functional/config.ccs.ts - x-pack/test/functional/config.firefox.js + - x-pack/test/functional/config.upgrade_assistant.ts - x-pack/test/functional_cloud/config.ts - x-pack/test/kubernetes_security/basic/config.ts - x-pack/test/licensing_plugin/config.public.ts diff --git a/.buildkite/pipelines/pull_request/security_solution.yml b/.buildkite/pipelines/pull_request/security_solution.yml index a30609ac5ca21..9444c821d5db1 100644 --- a/.buildkite/pipelines/pull_request/security_solution.yml +++ b/.buildkite/pipelines/pull_request/security_solution.yml @@ -12,3 +12,14 @@ steps: limit: 1 artifact_paths: - "target/kibana-security-solution/**/*" + + - command: .buildkite/scripts/steps/functional/security_solution_burn.sh + label: 'Security Solution Cypress tests, burning changed specs' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + parallelism: 1 + soft_fail: true + artifact_paths: + - "target/kibana-security-solution/**/*" diff --git a/.buildkite/scripts/steps/functional/security_solution_burn.sh b/.buildkite/scripts/steps/functional/security_solution_burn.sh new file mode 100755 index 0000000000000..f8b809dbbdac1 --- /dev/null +++ b/.buildkite/scripts/steps/functional/security_solution_burn.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/steps/functional/common.sh +source .buildkite/scripts/steps/functional/common_cypress.sh + +export JOB=kibana-security-solution-chrome +export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} + +buildkite-agent meta-data set "${BUILDKITE_JOB_ID}_is_test_execution_step" 'false' + +echo "--- Security Solution Cypress tests, burning changed specs (Chrome)" + +yarn --cwd x-pack/plugins/security_solution cypress:changed-specs-only diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dfcda878c2e4e..15a828ac76a94 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -589,6 +589,7 @@ src/plugins/screenshot_mode @elastic/appex-sharedux x-pack/examples/screenshotting_example @elastic/appex-sharedux x-pack/plugins/screenshotting @elastic/kibana-reporting-services examples/search_examples @elastic/kibana-data-discovery +packages/kbn-search-response-warnings @elastic/kibana-data-discovery x-pack/plugins/searchprofiler @elastic/platform-deployment-management x-pack/test/security_api_integration/packages/helpers @elastic/kibana-core x-pack/plugins/security @elastic/kibana-security @@ -1098,6 +1099,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /x-pack/plugins/security_solution/server/lib/timeline @elastic/security-threat-hunting-investigations ## Security Solution sub teams - Threat Hunting Explore +/x-pack/plugins/security_solution/common/api/tags @elastic/security-threat-hunting-explore /x-pack/plugins/security_solution/common/api/risk_score @elastic/security-threat-hunting-explore /x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts @elastic/security-threat-hunting-explore /x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram @elastic/security-threat-hunting-explore @@ -1145,6 +1147,11 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /x-pack/plugins/stack_connectors/server/connector_types/gen_ai @elastic/security-threat-hunting-explore /x-pack/plugins/stack_connectors/common/gen_ai @elastic/security-threat-hunting-explore +## Defend Workflows owner connectors +/x-pack/plugins/stack_connectors/public/connector_types/sentinelone @elastic/security-defend-workflows +/x-pack/plugins/stack_connectors/server/connector_types/sentinelone @elastic/security-defend-workflows +/x-pack/plugins/stack_connectors/common/sentinelone @elastic/security-defend-workflows + ## Security Solution sub teams - Detection Rule Management /x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema @elastic/security-detection-rule-management @elastic/security-detection-engine /x-pack/plugins/security_solution/common/api/detection_engine/fleet_integrations @elastic/security-detection-rule-management diff --git a/.i18nrc.json b/.i18nrc.json index 8ae8df0439409..02337d5b8f0d4 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -92,6 +92,7 @@ "server": "src/legacy/server", "share": "src/plugins/share", "sharedUXPackages": "packages/shared-ux", + "searchResponseWarnings": "packages/kbn-search-response-warnings", "securitySolutionPackages": "x-pack/packages/security-solution", "serverlessPackages": "packages/serverless", "coloring": "packages/kbn-coloring/src", diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 0b5c0d0bc3634..baec139453143 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -57,7 +57,11 @@ yarn_install( quiet = False, frozen_lockfile = False, environment = { + "GECKODRIVER_CDNURL": "https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache", + "CHROMEDRIVER_CDNURL": "https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache", + "CHROMEDRIVER_CDNBINARIESURL": "https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache", "SASS_BINARY_SITE": "https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-sass", "RE2_DOWNLOAD_MIRROR": "https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2", + "CYPRESS_DOWNLOAD_MIRROR": "https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/cypress", } ) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 4830d43d5a5e1..86783d0ca2341 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-08-08 +date: 2023-08-10 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 cbdf72e9b3f17..3bad125ce9a95 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.devdocs.json b/api_docs/aiops.devdocs.json index b4ce7cfd2e208..a5d2c0392ddfb 100644 --- a/api_docs/aiops.devdocs.json +++ b/api_docs/aiops.devdocs.json @@ -591,6 +591,69 @@ "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.presentationUtil", + "type": "Object", + "tags": [], + "label": "presentationUtil", + "description": [], + "signature": [ + { + "pluginId": "presentationUtil", + "scope": "public", + "docId": "kibPresentationUtilPluginApi", + "section": "def-public.PresentationUtilPluginStart", + "text": "PresentationUtilPluginStart" + }, + " | undefined" + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.embeddable", + "type": "Object", + "tags": [], + "label": "embeddable", + "description": [], + "signature": [ + { + "pluginId": "embeddable", + "scope": "public", + "docId": "kibEmbeddablePluginApi", + "section": "def-public.EmbeddableStart", + "text": "EmbeddableStart" + }, + " | undefined" + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.cases", + "type": "Object", + "tags": [], + "label": "cases", + "description": [], + "signature": [ + { + "pluginId": "cases", + "scope": "public", + "docId": "kibCasesPluginApi", + "section": "def-public.CasesUiStart", + "text": "CasesUiStart" + }, + " | undefined" + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -895,29 +958,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "aiops", - "id": "def-public.LogRateAnalysisContentWrapperProps.analysisType", - "type": "CompoundType", - "tags": [], - "label": "analysisType", - "description": [ - "The type of analysis, whether it's a spike or dip" - ], - "signature": [ - { - "pluginId": "aiops", - "scope": "common", - "docId": "kibAiopsPluginApi", - "section": "def-common.LogRateAnalysisType", - "text": "LogRateAnalysisType" - }, - " | undefined" - ], - "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "aiops", "id": "def-public.LogRateAnalysisContentWrapperProps.stickyHistogram", @@ -1127,6 +1167,22 @@ "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "aiops", + "id": "def-public.LogRateAnalysisResultsData.analysisType", + "type": "CompoundType", + "tags": [], + "label": "analysisType", + "description": [ + "The type of analysis, whether it's a spike or dip" + ], + "signature": [ + "\"spike\" | \"dip\"" + ], + "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "aiops", "id": "def-public.LogRateAnalysisResultsData.significantTerms", @@ -1178,44 +1234,8 @@ } ], "enums": [], - "misc": [ - { - "parentPluginId": "aiops", - "id": "def-public.LogRateAnalysisType", - "type": "Type", - "tags": [], - "label": "LogRateAnalysisType", - "description": [ - "\nUnion type of log rate analysis types." - ], - "signature": [ - "\"spike\" | \"dip\"" - ], - "path": "x-pack/plugins/aiops/common/constants.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], - "objects": [ - { - "parentPluginId": "aiops", - "id": "def-public.LOG_RATE_ANALYSIS_TYPE", - "type": "Object", - "tags": [], - "label": "LOG_RATE_ANALYSIS_TYPE", - "description": [ - "\nThe type of log rate analysis (spike or dip) will affect how parameters are\npassed to the analysis API endpoint." - ], - "signature": [ - "{ readonly SPIKE: \"spike\"; readonly DIP: \"dip\"; }" - ], - "path": "x-pack/plugins/aiops/common/constants.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ] + "misc": [], + "objects": [] }, "server": { "classes": [], @@ -1280,23 +1300,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "aiops", - "id": "def-common.LogRateAnalysisType", - "type": "Type", - "tags": [], - "label": "LogRateAnalysisType", - "description": [ - "\nUnion type of log rate analysis types." - ], - "signature": [ - "\"spike\" | \"dip\"" - ], - "path": "x-pack/plugins/aiops/common/constants.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "aiops", "id": "def-common.PLUGIN_ID", diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 370b7d66dafcd..9c5930a694dc4 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; @@ -21,22 +21,16 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 60 | 1 | 0 | 0 | +| 60 | 1 | 3 | 0 | ## Client -### Objects - - ### Functions ### Interfaces -### Consts, variables and types - - ## Server ### Setup diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index d0680f21fb288..3998967eaef3e 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -3303,7 +3303,7 @@ "section": "def-common.FieldSpec", "text": "FieldSpec" }, - "[]>; getFieldsForIndexPattern: (indexPattern: ", + "[]>; getExistingIndices: (indices: string[]) => Promise; getFieldsForIndexPattern: (indexPattern: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index b6e940f0c1c8f..a6dba1264568f 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-08-08 +date: 2023-08-10 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 f54b33efb04e3..1ec2a49338bdc 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -81,7 +81,7 @@ "label": "featureFlags", "description": [], "signature": [ - "{ agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; fastRefreshAvailable: boolean; }" + "{ agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; }" ], "path": "x-pack/plugins/apm/public/index.ts", "deprecated": false, @@ -265,7 +265,7 @@ "Observable", "; enabled: boolean; autoCreateApmDataView: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; serviceMapTerminateAfter: number; serviceMapMaxTraces: number; ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", - "; telemetryCollectionEnabled: boolean; metricsInterval: number; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; forceSyntheticSource: boolean; latestAgentVersionsUrl: string; serverlessOnboarding: boolean; serverless: Readonly<{} & { enabled: true; }>; managedServiceUrl: string; featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; fastRefreshAvailable: boolean; }>; }>>; getApmIndices: () => Promise>; createApmEventClient: ({ request, context, debug, }: { debug?: boolean | undefined; request: ", + "; telemetryCollectionEnabled: boolean; metricsInterval: number; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; forceSyntheticSource: boolean; latestAgentVersionsUrl: string; serverlessOnboarding: boolean; serverless: Readonly<{} & { enabled: true; }>; managedServiceUrl: string; featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; }>; }>>; getApmIndices: () => Promise>; createApmEventClient: ({ request, context, debug, }: { debug?: boolean | undefined; request: ", { "pluginId": "@kbn/core-http-server", "scope": "common", @@ -511,7 +511,7 @@ "signature": [ "{ readonly indices: Readonly<{} & { error: string; metric: string; span: string; transaction: string; onboarding: string; }>; readonly enabled: boolean; readonly autoCreateApmDataView: boolean; readonly serviceMapEnabled: boolean; readonly serviceMapFingerprintBucketSize: number; readonly serviceMapFingerprintGlobalBucketSize: number; readonly serviceMapTraceIdBucketSize: number; readonly serviceMapTraceIdGlobalBucketSize: number; readonly serviceMapMaxTracesPerRequest: number; readonly serviceMapTerminateAfter: number; readonly serviceMapMaxTraces: number; readonly ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; readonly searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", - "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; readonly forceSyntheticSource: boolean; readonly latestAgentVersionsUrl: string; readonly serverlessOnboarding: boolean; readonly serverless: Readonly<{} & { enabled: true; }>; readonly managedServiceUrl: string; readonly featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; fastRefreshAvailable: boolean; }>; }" + "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; readonly forceSyntheticSource: boolean; readonly latestAgentVersionsUrl: string; readonly serverlessOnboarding: boolean; readonly serverless: Readonly<{} & { enabled: true; }>; readonly managedServiceUrl: string; readonly featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; }>; }" ], "path": "x-pack/plugins/apm/server/routes/typings.ts", "deprecated": false, @@ -525,7 +525,7 @@ "label": "featureFlags", "description": [], "signature": [ - "{ agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; fastRefreshAvailable: boolean; }" + "{ agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; }" ], "path": "x-pack/plugins/apm/server/routes/typings.ts", "deprecated": false, @@ -963,7 +963,7 @@ "signature": [ "{ readonly indices: Readonly<{} & { error: string; metric: string; span: string; transaction: string; onboarding: string; }>; readonly enabled: boolean; readonly autoCreateApmDataView: boolean; readonly serviceMapEnabled: boolean; readonly serviceMapFingerprintBucketSize: number; readonly serviceMapFingerprintGlobalBucketSize: number; readonly serviceMapTraceIdBucketSize: number; readonly serviceMapTraceIdGlobalBucketSize: number; readonly serviceMapMaxTracesPerRequest: number; readonly serviceMapTerminateAfter: number; readonly serviceMapMaxTraces: number; readonly ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; readonly searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", - "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; readonly forceSyntheticSource: boolean; readonly latestAgentVersionsUrl: string; readonly serverlessOnboarding: boolean; readonly serverless: Readonly<{} & { enabled: true; }>; readonly managedServiceUrl: string; readonly featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; fastRefreshAvailable: boolean; }>; }" + "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; readonly forceSyntheticSource: boolean; readonly latestAgentVersionsUrl: string; readonly serverlessOnboarding: boolean; readonly serverless: Readonly<{} & { enabled: true; }>; readonly managedServiceUrl: string; readonly featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; }>; }" ], "path": "x-pack/plugins/apm/server/index.ts", "deprecated": false, @@ -1795,11 +1795,11 @@ "TypeC", "<{ useSpanName: ", "Type", - "; enableServiceTransactionMetrics: ", + "; enableServiceTransactionMetrics: ", "Type", - "; enableContinuousRollups: ", + "; enableContinuousRollups: ", "Type", - "; }>, ", + "; }>, ", "TypeC", "<{ kuery: ", "StringC", @@ -3169,7 +3169,7 @@ "StringC", "; searchServiceDestinationMetrics: ", "Type", - "; }>]>; }> | undefined; handler: ({}: ", + "; }>]>; }> | undefined; handler: ({}: ", { "pluginId": "apm", "scope": "server", @@ -3203,7 +3203,7 @@ "StringC", "; searchServiceDestinationMetrics: ", "Type", - "; }>, ", + "; }>, ", "TypeC", "<{ start: ", "Type", @@ -3269,7 +3269,7 @@ "StringC", "; searchServiceDestinationMetrics: ", "Type", - "; }>, ", + "; }>, ", "TypeC", "<{ start: ", "Type", @@ -3337,7 +3337,7 @@ "StringC", "; searchServiceDestinationMetrics: ", "Type", - "; }>, ", + "; }>, ", "TypeC", "<{ start: ", "Type", @@ -4115,7 +4115,7 @@ "PartialC", "<{ overwrite: ", "Type", - "; }>; }>, ", + "; }>; }>, ", "TypeC", "<{ body: ", "IntersectionC", @@ -4903,7 +4903,7 @@ "Type", "; useDurationSummary: ", "Type", - "; }>, ", + "; }>, ", "PartialC", "<{ transactionName: ", "StringC", @@ -5089,7 +5089,7 @@ "Type", "; useDurationSummary: ", "Type", - "; }>]>, ", + "; }>]>, ", "TypeC", "<{ transactionNames: ", "Type", @@ -5181,7 +5181,7 @@ "TypeC", "<{ useDurationSummary: ", "Type", - "; transactionType: ", + "; transactionType: ", "StringC", "; latencyAggregationType: ", "UnionC", @@ -8116,7 +8116,7 @@ "Observable", "; enabled: boolean; autoCreateApmDataView: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; serviceMapTerminateAfter: number; serviceMapMaxTraces: number; ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", - "; telemetryCollectionEnabled: boolean; metricsInterval: number; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; forceSyntheticSource: boolean; latestAgentVersionsUrl: string; serverlessOnboarding: boolean; serverless: Readonly<{} & { enabled: true; }>; managedServiceUrl: string; featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; fastRefreshAvailable: boolean; }>; }>>" + "; telemetryCollectionEnabled: boolean; metricsInterval: number; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; forceSyntheticSource: boolean; latestAgentVersionsUrl: string; serverlessOnboarding: boolean; serverless: Readonly<{} & { enabled: true; }>; managedServiceUrl: string; featureFlags: Readonly<{} & { agentConfigurationAvailable: boolean; configurableIndicesAvailable: boolean; infrastructureTabAvailable: boolean; infraUiAvailable: boolean; migrationToFleetAvailable: boolean; sourcemapApiAvailable: boolean; storageExplorerAvailable: boolean; }>; }>>" ], "path": "x-pack/plugins/apm/server/types.ts", "deprecated": false, diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index f9d18e273f7c8..4b9ffe1f2e5ac 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index f4d18f9705670..c8b9efdabad36 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 40c84072b6875..17ab8f80be077 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-08-08 +date: 2023-08-10 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 a09991262233c..e7757cb287952 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-08-08 +date: 2023-08-10 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 6015bb6745617..b4a40940e23de 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.devdocs.json b/api_docs/cases.devdocs.json index c0de3e16ac6a6..b749532e5f360 100644 --- a/api_docs/cases.devdocs.json +++ b/api_docs/cases.devdocs.json @@ -1091,7 +1091,7 @@ "\nReturn the UI capabilities for each type of operation. These strings must match the values defined in the UI\nhere: x-pack/plugins/cases/public/client/helpers/capabilities.ts" ], "signature": [ - "() => { all: readonly [\"create_cases\", \"read_cases\", \"update_cases\", \"push_cases\"]; read: readonly [\"read_cases\"]; delete: readonly [\"delete_cases\"]; }" + "() => { all: readonly [\"create_cases\", \"read_cases\", \"update_cases\", \"push_cases\", \"cases_connectors\"]; read: readonly [\"read_cases\", \"cases_connectors\"]; delete: readonly [\"delete_cases\"]; }" ], "path": "x-pack/plugins/cases/common/utils/capabilities.ts", "deprecated": false, @@ -1108,7 +1108,7 @@ "label": "getApiTags", "description": [], "signature": [ - "(owner: \"cases\" | \"observability\" | \"securitySolution\") => { all: readonly [\"casesSuggestUserProfiles\", \"bulkGetUserProfiles\", string, string]; read: readonly [\"casesSuggestUserProfiles\", \"bulkGetUserProfiles\", string]; delete: readonly [string]; }" + "(owner: \"cases\" | \"observability\" | \"securitySolution\") => { all: readonly [\"casesSuggestUserProfiles\", \"bulkGetUserProfiles\", \"casesGetConnectorsConfigure\", string, string]; read: readonly [\"casesSuggestUserProfiles\", \"bulkGetUserProfiles\", \"casesGetConnectorsConfigure\", string]; delete: readonly [string]; }" ], "path": "x-pack/plugins/cases/common/utils/api_tags.ts", "deprecated": false, @@ -1312,6 +1312,17 @@ "path": "x-pack/plugins/cases/common/ui/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesPermissions.connectors", + "type": "boolean", + "tags": [], + "label": "connectors", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 5b9296c2f8cac..a40e4198326a2 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 93 | 0 | 74 | 27 | +| 94 | 0 | 75 | 27 | ## Client diff --git a/api_docs/charts.devdocs.json b/api_docs/charts.devdocs.json index abf1df8307859..b8c332abcc153 100644 --- a/api_docs/charts.devdocs.json +++ b/api_docs/charts.devdocs.json @@ -1350,7 +1350,15 @@ "section": "def-common.Datatable", "text": "Datatable" }, - ", \"rows\" | \"columns\">; column: number; value: any[]; }; timeFieldName?: string | undefined; negate?: boolean | undefined; }" + ", \"rows\" | \"columns\">; cells: { column: number; row: number; }[]; relation?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.BooleanRelation", + "text": "BooleanRelation" + }, + " | undefined; }[]; timeFieldName?: string | undefined; negate?: boolean | undefined; }" ], "path": "src/plugins/charts/public/index.ts", "deprecated": false, diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 0386ec84baa3e..9ddb4d651b7b3 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-08-08 +date: 2023-08-10 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 917ce1dd5e70d..257ae7fc83e29 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index 2439ac29b2acd..2f8dfaa9ba409 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_chat_provider.mdx b/api_docs/cloud_chat_provider.mdx index 09e274f8d6ac2..bb35de3db20ac 100644 --- a/api_docs/cloud_chat_provider.mdx +++ b/api_docs/cloud_chat_provider.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChatProvider title: "cloudChatProvider" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChatProvider plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChatProvider'] --- import cloudChatProviderObj from './cloud_chat_provider.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 904c4a2398b07..d49d837df6d07 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index 50651cb15c6eb..1b083e68a4f3d 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 19630e2129c64..948087b9508f8 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index e5d9d6c84093e..5746fc75f656f 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-08-08 +date: 2023-08-10 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 c26eb4445b0d2..584fc4bafb0c9 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 375156d7a08a5..ad7ba3b1ac02f 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index b86db1ce03475..aaa9ba738aa2d 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 48852fd3e96b2..853c2d8170e26 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-08-08 +date: 2023-08-10 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 2bb3f88bcfd8e..b68f871091838 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-08-08 +date: 2023-08-10 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 f5efa0a0d205f..8139bcd458a66 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 0ff7fe1f1bdc7..4f47b2094fb4d 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -12999,7 +12999,7 @@ "section": "def-server.DataPluginStart", "text": "DataPluginStart" }, - ">, { bfetch, expressions, usageCollection, fieldFormats, taskManager, security, }: ", + ">, { bfetch, expressions, usageCollection, fieldFormats, security }: ", "DataPluginSetupDependencies", ") => { search: ", "ISearchSetup", @@ -13056,7 +13056,7 @@ "id": "def-server.DataServerPlugin.setup.$2", "type": "Object", "tags": [], - "label": "{\n bfetch,\n expressions,\n usageCollection,\n fieldFormats,\n taskManager,\n security,\n }", + "label": "{ bfetch, expressions, usageCollection, fieldFormats, security }", "description": [], "signature": [ "DataPluginSetupDependencies" @@ -13085,7 +13085,7 @@ "section": "def-common.CoreStart", "text": "CoreStart" }, - ", { fieldFormats, dataViews, taskManager }: ", + ", { fieldFormats, dataViews }: ", "DataPluginStartDependencies", ") => { datatableUtilities: ", "DatatableUtilitiesService", @@ -13155,7 +13155,7 @@ "id": "def-server.DataServerPlugin.start.$2", "type": "Object", "tags": [], - "label": "{ fieldFormats, dataViews, taskManager }", + "label": "{ fieldFormats, dataViews }", "description": [], "signature": [ "DataPluginStartDependencies" @@ -13568,6 +13568,10 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/public/update_data_view.ts" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts" @@ -13604,10 +13608,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -16322,6 +16322,44 @@ "FieldSpec[]" ] }, + { + "parentPluginId": "data", + "id": "def-server.DataViewsService.getExistingIndices", + "type": "Function", + "tags": [], + "label": "getExistingIndices", + "description": [ + "\nGet existing index pattern list by providing string array index pattern list." + ], + "signature": [ + "(indices: string[]) => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-server.DataViewsService.getExistingIndices.$1", + "type": "Array", + "tags": [], + "label": "indices", + "description": [ + "index pattern list" + ], + "signature": [ + "string[]" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "index pattern list" + ] + }, { "parentPluginId": "data", "id": "def-server.DataViewsService.getFieldsForIndexPattern", @@ -17299,6 +17337,21 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "data", + "id": "def-server.IndexPatternsFetcher.Unnamed.$3", + "type": "boolean", + "tags": [], + "label": "rollupsEnabled", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [] @@ -21247,6 +21300,10 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/public/update_data_view.ts" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts" @@ -21283,10 +21340,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -24872,6 +24925,44 @@ "FieldSpec[]" ] }, + { + "parentPluginId": "data", + "id": "def-common.DataViewsService.getExistingIndices", + "type": "Function", + "tags": [], + "label": "getExistingIndices", + "description": [ + "\nGet existing index pattern list by providing string array index pattern list." + ], + "signature": [ + "(indices: string[]) => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.DataViewsService.getExistingIndices.$1", + "type": "Array", + "tags": [], + "label": "indices", + "description": [ + "index pattern list" + ], + "signature": [ + "string[]" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "index pattern list" + ] + }, { "parentPluginId": "data", "id": "def-common.DataViewsService.getFieldsForIndexPattern", @@ -28292,7 +28383,7 @@ "section": "def-common.FieldSpec", "text": "FieldSpec" }, - "[]>; getFieldsForIndexPattern: (indexPattern: ", + "[]>; getExistingIndices: (indices: string[]) => Promise; getFieldsForIndexPattern: (indexPattern: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index ed440a37583cb..d48b3a6a3e082 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3295 | 119 | 2573 | 27 | +| 3301 | 119 | 2575 | 27 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 6f4cdc678a730..d3a2a0dc456e3 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3295 | 119 | 2573 | 27 | +| 3301 | 119 | 2575 | 27 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index b4c44781b50fa..2a246bcc16312 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -3889,6 +3889,20 @@ "path": "src/plugins/data/server/search/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "data", + "id": "def-server.SearchStrategyDependencies.rollupsEnabled", + "type": "CompoundType", + "tags": [], + "label": "rollupsEnabled", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/server/search/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 2ba7fae435d5f..a54d159e299bb 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3295 | 119 | 2573 | 27 | +| 3301 | 119 | 2575 | 27 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 0afb9c6cc38d6..765d65b328eb1 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-08-08 +date: 2023-08-10 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 88b56f7be7bf5..81f5c42e6652e 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-08-08 +date: 2023-08-10 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 4a6c796e4e163..6708b3bbd5e58 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index c82f54e715564..cb3003d20cac3 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -379,6 +379,10 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts" @@ -415,10 +419,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -4410,6 +4410,44 @@ "FieldSpec[]" ] }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsService.getExistingIndices", + "type": "Function", + "tags": [], + "label": "getExistingIndices", + "description": [ + "\nGet existing index pattern list by providing string array index pattern list." + ], + "signature": [ + "(indices: string[]) => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsService.getExistingIndices.$1", + "type": "Array", + "tags": [], + "label": "indices", + "description": [ + "index pattern list" + ], + "signature": [ + "string[]" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "index pattern list" + ] + }, { "parentPluginId": "dataViews", "id": "def-public.DataViewsService.getFieldsForIndexPattern", @@ -8229,6 +8267,10 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts" @@ -8265,10 +8307,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -10576,7 +10614,7 @@ "section": "def-server.DataViewsServerPluginSetupDependencies", "text": "DataViewsServerPluginSetupDependencies" }, - ") => {}" + ") => { enableRollups: () => boolean; }" ], "path": "src/plugins/data_views/server/plugin.ts", "deprecated": false, @@ -11301,6 +11339,44 @@ "FieldSpec[]" ] }, + { + "parentPluginId": "dataViews", + "id": "def-server.DataViewsService.getExistingIndices", + "type": "Function", + "tags": [], + "label": "getExistingIndices", + "description": [ + "\nGet existing index pattern list by providing string array index pattern list." + ], + "signature": [ + "(indices: string[]) => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-server.DataViewsService.getExistingIndices.$1", + "type": "Array", + "tags": [], + "label": "indices", + "description": [ + "index pattern list" + ], + "signature": [ + "string[]" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "index pattern list" + ] + }, { "parentPluginId": "dataViews", "id": "def-server.DataViewsService.getFieldsForIndexPattern", @@ -12278,6 +12354,21 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "dataViews", + "id": "def-server.IndexPatternsFetcher.Unnamed.$3", + "type": "boolean", + "tags": [], + "label": "rollupsEnabled", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [] @@ -13451,6 +13542,39 @@ } ], "objects": [], + "setup": { + "parentPluginId": "dataViews", + "id": "def-server.DataViewsServerPluginSetup", + "type": "Interface", + "tags": [], + "label": "DataViewsServerPluginSetup", + "description": [ + "\nDataViews server plugin setup api" + ], + "path": "src/plugins/data_views/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-server.DataViewsServerPluginSetup.enableRollups", + "type": "Function", + "tags": [], + "label": "enableRollups", + "description": [], + "signature": [ + "() => void" + ], + "path": "src/plugins/data_views/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "lifecycle": "setup", + "initialIsOpen": true + }, "start": { "parentPluginId": "dataViews", "id": "def-server.DataViewsServerPluginStart", @@ -14803,22 +14927,6 @@ ], "lifecycle": "start", "initialIsOpen": true - }, - "setup": { - "parentPluginId": "dataViews", - "id": "def-server.DataViewsServerPluginSetup", - "type": "Interface", - "tags": [], - "label": "DataViewsServerPluginSetup", - "description": [ - "\nDataViews server plugin setup api" - ], - "path": "src/plugins/data_views/server/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "lifecycle": "setup", - "initialIsOpen": true } }, "common": { @@ -15200,6 +15308,10 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts" @@ -15236,10 +15348,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -18936,6 +19044,44 @@ "FieldSpec[]" ] }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsService.getExistingIndices", + "type": "Function", + "tags": [], + "label": "getExistingIndices", + "description": [ + "\nGet existing index pattern list by providing string array index pattern list." + ], + "signature": [ + "(indices: string[]) => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsService.getExistingIndices.$1", + "type": "Array", + "tags": [], + "label": "indices", + "description": [ + "index pattern list" + ], + "signature": [ + "string[]" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "index pattern list" + ] + }, { "parentPluginId": "dataViews", "id": "def-common.DataViewsService.getFieldsForIndexPattern", @@ -22036,6 +22182,44 @@ ], "returnComment": [] }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsServicePublicMethods.getExistingIndices", + "type": "Function", + "tags": [], + "label": "getExistingIndices", + "description": [ + "\nGet existing index pattern list by providing string array index pattern list." + ], + "signature": [ + "(indices: string[]) => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsServicePublicMethods.getExistingIndices.$1", + "type": "Array", + "tags": [], + "label": "indices", + "description": [ + "- index pattern list" + ], + "signature": [ + "string[]" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "index pattern list of index patterns that match indices" + ] + }, { "parentPluginId": "dataViews", "id": "def-common.DataViewsServicePublicMethods.getIds", @@ -24907,7 +25091,7 @@ "section": "def-common.FieldSpec", "text": "FieldSpec" }, - "[]>; getFieldsForIndexPattern: (indexPattern: ", + "[]>; getExistingIndices: (indices: string[]) => Promise; getFieldsForIndexPattern: (indexPattern: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 939dbf86bcc32..4d03a36b92878 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1012 | 0 | 243 | 2 | +| 1024 | 0 | 246 | 2 | ## Client diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 121bea50c1589..1336377ffa864 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-08-08 +date: 2023-08-10 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 acc2067839829..64b0db70a4bdc 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -18,12 +18,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | ml, stackAlerts | - | | | ruleRegistry, observability, ml, infra, monitoring, securitySolution, stackAlerts, synthetics, transform, uptime | - | -| | @kbn/es-query, @kbn/visualization-ui-components, observability, securitySolution, timelines, lists, threatIntelligence, savedSearch, dataViews, savedObjectsManagement, unifiedSearch, controls, @kbn/unified-field-list, @kbn/event-annotation-components, lens, ml, logsShared, visTypeTimeseries, apm, triggersActionsUi, exploratoryView, fleet, dataVisualizer, stackAlerts, infra, canvas, enterpriseSearch, graph, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, data | - | -| | @kbn/es-query, @kbn/visualization-ui-components, observability, securitySolution, timelines, lists, threatIntelligence, savedSearch, dataViews, savedObjectsManagement, unifiedSearch, controls, @kbn/unified-field-list, @kbn/event-annotation-components, lens, ml, logsShared, visTypeTimeseries, apm, triggersActionsUi, exploratoryView, fleet, dataVisualizer, stackAlerts, infra, canvas, enterpriseSearch, graph, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, data | - | -| | @kbn/es-query, @kbn/visualization-ui-components, observability, securitySolution, timelines, lists, threatIntelligence, savedSearch, data, savedObjectsManagement, unifiedSearch, controls, @kbn/unified-field-list, @kbn/event-annotation-components, lens, ml, logsShared, visTypeTimeseries, apm, triggersActionsUi, exploratoryView, fleet, dataVisualizer, stackAlerts, infra, canvas, enterpriseSearch, graph, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega | - | -| | inspector, data, advancedSettings, savedObjects, embeddable, dataViewEditor, unifiedSearch, visualizations, controls, dashboard, licensing, savedObjectsTagging, eventAnnotation, dataViewFieldEditor, lens, @kbn/ml-date-picker, aiops, security, triggersActionsUi, cases, observabilityShared, discover, exploratoryView, fleet, maps, telemetry, dataVisualizer, ml, observability, banners, reporting, timelines, runtimeFields, indexManagement, dashboardEnhanced, imageEmbeddable, graph, monitoring, securitySolution, synthetics, transform, uptime, console, dataViewManagement, filesManagement, uiActions, visTypeVislib | - | +| | @kbn/es-query, @kbn/visualization-ui-components, observability, securitySolution, timelines, lists, threatIntelligence, savedSearch, dataViews, savedObjectsManagement, unifiedSearch, controls, @kbn/unified-field-list, @kbn/event-annotation-components, lens, triggersActionsUi, ml, logsShared, visTypeTimeseries, apm, exploratoryView, fleet, dataVisualizer, stackAlerts, infra, canvas, enterpriseSearch, graph, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, data | - | +| | @kbn/es-query, @kbn/visualization-ui-components, observability, securitySolution, timelines, lists, threatIntelligence, savedSearch, dataViews, savedObjectsManagement, unifiedSearch, controls, @kbn/unified-field-list, @kbn/event-annotation-components, lens, triggersActionsUi, ml, logsShared, visTypeTimeseries, apm, exploratoryView, fleet, dataVisualizer, stackAlerts, infra, canvas, enterpriseSearch, graph, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, data | - | +| | @kbn/es-query, @kbn/visualization-ui-components, observability, securitySolution, timelines, lists, threatIntelligence, savedSearch, data, savedObjectsManagement, unifiedSearch, controls, @kbn/unified-field-list, @kbn/event-annotation-components, lens, triggersActionsUi, ml, logsShared, visTypeTimeseries, apm, exploratoryView, fleet, dataVisualizer, stackAlerts, infra, canvas, enterpriseSearch, graph, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega | - | +| | inspector, data, advancedSettings, savedObjects, embeddable, dataViewEditor, unifiedSearch, visualizations, controls, dashboard, licensing, savedObjectsTagging, eventAnnotation, dataViewFieldEditor, lens, security, triggersActionsUi, cases, @kbn/ml-date-picker, aiops, observabilityShared, discover, exploratoryView, fleet, maps, telemetry, dataVisualizer, ml, observability, banners, reporting, timelines, cloudSecurityPosture, runtimeFields, indexManagement, dashboardEnhanced, imageEmbeddable, graph, monitoring, securitySolution, synthetics, transform, uptime, cloudLinks, console, dataViewManagement, filesManagement, uiActions, visTypeVislib | - | | | home, data, esUiShared, savedObjectsManagement, exploratoryView, fleet, ml, observability, apm, indexLifecycleManagement, observabilityOnboarding, synthetics, upgradeAssistant, uptime, ux, kibanaOverview | - | -| | share, uiActions, guidedOnboarding, home, management, data, advancedSettings, spaces, savedObjects, visualizations, controls, dashboard, savedObjectsTagging, expressionXY, lens, expressionMetricVis, expressionGauge, alerting, security, triggersActionsUi, serverless, cases, discover, exploratoryView, fleet, maps, licenseManagement, dataVisualizer, ml, observability, infra, profiling, apm, canvas, expressionImage, expressionMetric, expressionError, expressionRevealImage, expressionRepeatImage, expressionShape, indexManagement, crossClusterReplication, enterpriseSearch, globalSearchBar, graph, grokdebugger, indexLifecycleManagement, ingestPipelines, logstash, monitoring, observabilityOnboarding, osquery, devTools, painlessLab, remoteClusters, rollup, searchprofiler, newsfeed, securitySolution, serverlessSearch, snapshotRestore, synthetics, transform, upgradeAssistant, uptime, ux, watcher, cloudDataMigration, console, dataViewManagement, filesManagement, kibanaOverview, visDefaultEditor, expressionHeatmap, expressionLegacyMetricVis, expressionPartitionVis, expressionTagcloud, visTypeTable, visTypeTimelion, visTypeTimeseries, visTypeVega, visTypeVislib | - | +| | share, uiActions, guidedOnboarding, home, management, data, advancedSettings, spaces, savedObjects, visualizations, serverless, controls, dashboard, savedObjectsTagging, expressionXY, lens, expressionMetricVis, expressionGauge, security, alerting, triggersActionsUi, cases, aiops, discover, exploratoryView, observabilityAIAssistant, fleet, maps, licenseManagement, dataVisualizer, ml, observability, infra, profiling, apm, canvas, expressionImage, expressionMetric, expressionError, expressionRevealImage, expressionRepeatImage, expressionShape, indexManagement, crossClusterReplication, enterpriseSearch, globalSearchBar, graph, grokdebugger, indexLifecycleManagement, ingestPipelines, logstash, monitoring, observabilityOnboarding, osquery, devTools, painlessLab, remoteClusters, rollup, searchprofiler, newsfeed, securitySolution, serverlessSearch, snapshotRestore, synthetics, transform, upgradeAssistant, uptime, ux, watcher, cloudDataMigration, console, dataViewManagement, filesManagement, kibanaOverview, visDefaultEditor, expressionHeatmap, expressionLegacyMetricVis, expressionPartitionVis, expressionTagcloud, visTypeTable, visTypeTimelion, visTypeTimeseries, visTypeVega, visTypeVislib | - | | | encryptedSavedObjects, actions, data, ml, logstash, securitySolution, cloudChat | - | | | actions, ml, savedObjectsTagging, enterpriseSearch | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core, savedObjects, presentationUtil, visualizations, aiops, ml, dataVisualizer, dashboardEnhanced, graph, lens, securitySolution, eventAnnotation, @kbn/core-saved-objects-browser-mocks | - | @@ -102,6 +102,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement | - | +| | visualizations, graph | - | | | @kbn/core, lens, savedObjects | - | | | dataViews, maps | - | | | dataViewManagement, dataViews | - | @@ -140,7 +141,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/core-lifecycle-browser-mocks, @kbn/core, @kbn/core-plugins-browser-internal | - | | | @kbn/core | - | | | @kbn/core-plugins-server-internal | - | -| | security, licenseManagement, ml, profiling, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | 8.8.0 | +| | security, aiops, licenseManagement, ml, profiling, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | 8.8.0 | | | spaces, security, actions, alerting, ml, remoteClusters, graph, indexLifecycleManagement, mapsEms, osquery, painlessLab, rollup, searchprofiler, securitySolution, snapshotRestore, transform, upgradeAssistant | 8.8.0 | | | apm, fleet, security, securitySolution | 8.8.0 | | | apm, fleet, security, securitySolution | 8.8.0 | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index cdf90dc6b2602..a061b8f8d595c 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -399,7 +399,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [log_categorization_app_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx#:~:text=toMountPoint), [log_categorization_app_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx#:~:text=toMountPoint), [show_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx#:~:text=toMountPoint), [show_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx#:~:text=toMountPoint), [show_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx#:~:text=toMountPoint), [log_rate_analysis_app_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx#:~:text=toMountPoint), [log_rate_analysis_app_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx#:~:text=toMountPoint), [log_rate_analysis_content_wrapper.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx#:~:text=toMountPoint), [log_rate_analysis_content_wrapper.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx#:~:text=toMountPoint), [change_point_detection_root.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx#:~:text=toMountPoint)+ 1 more | - | +| | [embeddable_change_point_chart.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx#:~:text=toMountPoint), [embeddable_change_point_chart.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx#:~:text=toMountPoint), [handle_explicit_input.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx#:~:text=toMountPoint), [handle_explicit_input.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx#:~:text=toMountPoint), [log_categorization_app_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx#:~:text=toMountPoint), [log_categorization_app_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx#:~:text=toMountPoint), [show_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx#:~:text=toMountPoint), [show_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx#:~:text=toMountPoint), [show_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx#:~:text=toMountPoint), [log_rate_analysis_app_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx#:~:text=toMountPoint)+ 5 more | - | +| | [embeddable_change_point_chart.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx#:~:text=KibanaThemeProvider), [embeddable_change_point_chart.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx#:~:text=KibanaThemeProvider), [embeddable_change_point_chart.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx#:~:text=KibanaThemeProvider) | - | +| | [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/plugin.tsx#:~:text=license%24) | 8.8.0 | | | [search_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/application/utils/search_utils.ts#:~:text=SimpleSavedObject), [search_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/application/utils/search_utils.ts#:~:text=SimpleSavedObject) | - | @@ -502,11 +504,20 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] +## cloudLinks + +| Deprecated API | Reference location(s) | Remove By | +| ---------------|-----------|-----------| +| | [theme_darkmode_toggle.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx#:~:text=toMountPoint), [theme_darkmode_toggle.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx#:~:text=toMountPoint) | - | + + + ## cloudSecurityPosture | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [overview_tab.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/overview_tab.tsx#:~:text=indexPatternId) | - | +| | [take_action.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/public/components/take_action.tsx#:~:text=toMountPoint), [take_action.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/public/components/take_action.tsx#:~:text=toMountPoint) | - | @@ -946,6 +957,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [confirm_modal_promise.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/helpers/saved_objects_utils/confirm_modal_promise.tsx#:~:text=toMountPoint), [confirm_modal_promise.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/helpers/saved_objects_utils/confirm_modal_promise.tsx#:~:text=toMountPoint), [workspace_top_nav_menu.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/components/workspace_layout/workspace_top_nav_menu.tsx#:~:text=toMountPoint), [workspace_top_nav_menu.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/components/workspace_layout/workspace_top_nav_menu.tsx#:~:text=toMountPoint), [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/application.tsx#:~:text=toMountPoint), [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/application.tsx#:~:text=toMountPoint) | - | | | [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/application.tsx#:~:text=KibanaThemeProvider), [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/application.tsx#:~:text=KibanaThemeProvider), [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/application.tsx#:~:text=KibanaThemeProvider) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/server/plugin.ts#:~:text=license%24) | 8.8.0 | +| | [source_picker.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/components/source_picker.tsx#:~:text=includeFields) | - | | | [save_modal.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/components/save_modal.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/components/save_modal.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | | | [app_state.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/types/app_state.ts#:~:text=SimpleSavedObject), [app_state.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/types/app_state.ts#:~:text=SimpleSavedObject), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/helpers/saved_objects_utils/find_object_by_title.test.ts#:~:text=SimpleSavedObject), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/helpers/saved_objects_utils/find_object_by_title.test.ts#:~:text=SimpleSavedObject) | - | | | [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/helpers/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectsCreateOptions), [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/graph/public/helpers/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectsCreateOptions) | - | @@ -1203,7 +1215,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts#:~:text=title), [rollup.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/job_service/new_job_caps/rollup.ts#:~:text=title), [alerting_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts#:~:text=title), [data_recognizer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts#:~:text=title), [configuration_step_details.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_details.tsx#:~:text=title), [data_loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/datavisualizer/index_based/data_loader/data_loader.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title)+ 40 more | - | | | [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts#:~:text=title), [rollup.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/job_service/new_job_caps/rollup.ts#:~:text=title), [alerting_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts#:~:text=title), [data_recognizer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts#:~:text=title), [configuration_step_details.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_details.tsx#:~:text=title), [data_loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/datavisualizer/index_based/data_loader/data_loader.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title)+ 40 more | - | | | [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts#:~:text=title), [rollup.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/job_service/new_job_caps/rollup.ts#:~:text=title), [alerting_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts#:~:text=title), [data_recognizer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts#:~:text=title), [configuration_step_details.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_details.tsx#:~:text=title), [data_loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/datavisualizer/index_based/data_loader/data_loader.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title)+ 15 more | - | -| | [clone_action_name.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx#:~:text=toMountPoint), [clone_action_name.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx#:~:text=toMountPoint), [force_stop_dialog.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx#:~:text=toMountPoint), [force_stop_dialog.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx#:~:text=toMountPoint), [deployment_setup.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx#:~:text=toMountPoint), [deployment_setup.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx#:~:text=toMountPoint), [header_menu_portal.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/components/header_menu_portal/header_menu_portal.tsx#:~:text=toMountPoint), [header_menu_portal.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/components/header_menu_portal/header_menu_portal.tsx#:~:text=toMountPoint), [create_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/job_creation/common/create_flyout.tsx#:~:text=toMountPoint), [create_flyout.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/job_creation/common/create_flyout.tsx#:~:text=toMountPoint)+ 10 more | - | +| | [clone_action_name.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx#:~:text=toMountPoint), [clone_action_name.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx#:~:text=toMountPoint), [force_stop_dialog.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx#:~:text=toMountPoint), [force_stop_dialog.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx#:~:text=toMountPoint), [deployment_setup.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx#:~:text=toMountPoint), [deployment_setup.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx#:~:text=toMountPoint), [header_menu_portal.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/components/header_menu_portal/header_menu_portal.tsx#:~:text=toMountPoint), [header_menu_portal.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/components/header_menu_portal/header_menu_portal.tsx#:~:text=toMountPoint), [resolve_job_selection.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx#:~:text=toMountPoint), [resolve_job_selection.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx#:~:text=toMountPoint)+ 10 more | - | | | [jobs_list_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx#:~:text=RedirectAppLinks), [jobs_list_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx#:~:text=RedirectAppLinks), [jobs_list_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx#:~:text=RedirectAppLinks) | - | | | [anomaly_charts_embeddable.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx#:~:text=KibanaThemeProvider), [anomaly_charts_embeddable.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx#:~:text=KibanaThemeProvider), [anomaly_charts_embeddable.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx#:~:text=KibanaThemeProvider), [anomaly_swimlane_embeddable.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx#:~:text=KibanaThemeProvider), [anomaly_swimlane_embeddable.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx#:~:text=KibanaThemeProvider), [anomaly_swimlane_embeddable.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx#:~:text=KibanaThemeProvider), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/app.tsx#:~:text=KibanaThemeProvider), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/app.tsx#:~:text=KibanaThemeProvider), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/app.tsx#:~:text=KibanaThemeProvider), [jobs_list_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx#:~:text=KibanaThemeProvider)+ 2 more | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/plugin.ts#:~:text=license%24) | 8.8.0 | @@ -1257,6 +1269,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] +## observabilityAIAssistant + +| Deprecated API | Reference location(s) | Remove By | +| ---------------|-----------|-----------| +| | [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_ai_assistant/public/application.tsx#:~:text=KibanaThemeProvider), [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_ai_assistant/public/application.tsx#:~:text=KibanaThemeProvider), [application.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_ai_assistant/public/application.tsx#:~:text=KibanaThemeProvider) | - | + + + ## observabilityOnboarding | Deprecated API | Reference location(s) | Remove By | @@ -1462,7 +1482,7 @@ This is relied on by the reporting feature, and should be removed once reporting migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/issues/19914 | | | [app_authorization.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/app_authorization.ts#:~:text=getKibanaFeatures), [privileges.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.ts#:~:text=getKibanaFeatures), [authorization_service.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/authorization_service.tsx#:~:text=getKibanaFeatures), [app_authorization.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/app_authorization.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures)+ 15 more | 8.8.0 | | | [authorization_service.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/authorization_service.tsx#:~:text=getElasticsearchFeatures) | 8.8.0 | -| | [use_update_user_profile.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx#:~:text=toMountPoint), [use_update_user_profile.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx#:~:text=toMountPoint), [session_expiration_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/session/session_expiration_toast.tsx#:~:text=toMountPoint), [session_expiration_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/session/session_expiration_toast.tsx#:~:text=toMountPoint) | - | +| | [account_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/account_management/account_management_app.tsx#:~:text=toMountPoint), [account_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/account_management/account_management_app.tsx#:~:text=toMountPoint), [session_expiration_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/session/session_expiration_toast.tsx#:~:text=toMountPoint), [session_expiration_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/session/session_expiration_toast.tsx#:~:text=toMountPoint) | - | | | [access_agreement_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx#:~:text=KibanaThemeProvider), [access_agreement_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx#:~:text=KibanaThemeProvider), [access_agreement_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx#:~:text=KibanaThemeProvider), [logged_out_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx#:~:text=KibanaThemeProvider), [logged_out_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx#:~:text=KibanaThemeProvider), [logged_out_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx#:~:text=KibanaThemeProvider), [login_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/login/login_page.tsx#:~:text=KibanaThemeProvider), [login_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/login/login_page.tsx#:~:text=KibanaThemeProvider), [login_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/login/login_page.tsx#:~:text=KibanaThemeProvider), [overwritten_session_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx#:~:text=KibanaThemeProvider)+ 20 more | - | | | [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode), [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode), [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode) | 8.8.0 | | | [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/plugin.tsx#:~:text=license%24) | 8.8.0 | @@ -1818,6 +1838,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | ---------------|-----------|-----------| | | [confirm_modal_promise.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/confirm_modal_promise.tsx#:~:text=toMountPoint), [confirm_modal_promise.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/confirm_modal_promise.tsx#:~:text=toMountPoint), [use_visualize_app_state.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/utils/use/use_visualize_app_state.tsx#:~:text=toMountPoint), [use_visualize_app_state.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/utils/use/use_visualize_app_state.tsx#:~:text=toMountPoint), [visualize_no_match.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/components/visualize_no_match.tsx#:~:text=toMountPoint), [visualize_no_match.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/components/visualize_no_match.tsx#:~:text=toMountPoint), [index.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/index.tsx#:~:text=toMountPoint), [index.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/index.tsx#:~:text=toMountPoint) | - | | | [use_visualize_app_state.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/utils/use/use_visualize_app_state.tsx#:~:text=KibanaThemeProvider), [use_visualize_app_state.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/utils/use/use_visualize_app_state.tsx#:~:text=KibanaThemeProvider), [use_visualize_app_state.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/utils/use/use_visualize_app_state.tsx#:~:text=KibanaThemeProvider), [visualize_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx#:~:text=KibanaThemeProvider), [visualize_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx#:~:text=KibanaThemeProvider), [visualize_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx#:~:text=KibanaThemeProvider), [show_new_vis.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/show_new_vis.tsx#:~:text=KibanaThemeProvider), [show_new_vis.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/show_new_vis.tsx#:~:text=KibanaThemeProvider), [show_new_vis.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/show_new_vis.tsx#:~:text=KibanaThemeProvider), [visualize_no_match.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/components/visualize_no_match.tsx#:~:text=KibanaThemeProvider)+ 5 more | - | +| | [search_selection.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx#:~:text=includeFields), [visualize_embeddable_factory.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx#:~:text=includeFields) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/plugin.ts#:~:text=savedObjects), [visualize_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx#:~:text=savedObjects) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/plugin.ts#:~:text=SavedObjectsClientContract), [plugin.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/plugin.ts#:~:text=SavedObjectsClientContract) | - | | | [visualize_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx#:~:text=delete) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 585d6b75de9aa..dd54d80679b38 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -120,7 +120,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Plugin | Deprecated API | Reference location(s) | Remove By | | --------|-------|-----------|-----------| -| ml | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/plugin.ts#:~:text=license%24) | 8.8.0 | +| ml | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/plugin.ts#:~:text=license%24), [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/aiops/public/plugin.tsx#:~:text=license%24) | 8.8.0 | | ml | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/plugin.ts#:~:text=license%24), [license.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/transform/server/services/license.ts#:~:text=license%24) | 8.8.0 | diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 8a8540fc1360f..59884cb7a6a03 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-08-08 +date: 2023-08-10 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 2bd1d80e8a7b2..9a15eabe616c8 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-08-08 +date: 2023-08-10 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 bd072de583e44..2830cab0b59d7 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 7cf7dd6fd5681..7fd44fac756db 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index cbb78b0f3defe..be7678ec89f33 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -7589,6 +7589,26 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddableStartDependencies.contentManagement", + "type": "Object", + "tags": [], + "label": "contentManagement", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "public", + "docId": "kibContentManagementPluginApi", + "section": "def-public.ContentManagementPublicStart", + "text": "ContentManagementPublicStart" + } + ], + "path": "src/plugins/embeddable/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "embeddable", "id": "def-public.EmbeddableStartDependencies.savedObjectsManagement", @@ -9155,7 +9175,15 @@ "section": "def-common.Datatable", "text": "Datatable" }, - ", \"rows\" | \"columns\">; column: number; value: any[]; }; timeFieldName?: string | undefined; negate?: boolean | undefined; }" + ", \"rows\" | \"columns\">; cells: { column: number; row: number; }[]; relation?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.BooleanRelation", + "text": "BooleanRelation" + }, + " | undefined; }[]; timeFieldName?: string | undefined; negate?: boolean | undefined; }" ], "path": "src/plugins/embeddable/public/lib/triggers/triggers.ts", "deprecated": false, diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 5ca2ab1573fa9..59a2a6facb00c 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 535 | 11 | 437 | 7 | +| 536 | 11 | 438 | 7 | ## Client diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 9ec369a20e907..62400f8bfb19e 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-08-08 +date: 2023-08-10 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 fd78a0506ea65..5c703ad47f6a6 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-08-08 +date: 2023-08-10 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 142741b6ca029..b5b66595a7733 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-08-08 +date: 2023-08-10 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 f72c796652692..2b22c7c3fdbc9 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.devdocs.json b/api_docs/event_annotation.devdocs.json index e61c8b977dd39..c6ce5b7227966 100644 --- a/api_docs/event_annotation.devdocs.json +++ b/api_docs/event_annotation.devdocs.json @@ -461,7 +461,15 @@ "section": "def-common.SavedObjectCommon", "text": "SavedObjectCommon" }, - "; }) => void; onCreateNew: () => void; }) => JSX.Element" + "<", + { + "pluginId": "savedObjectsFinder", + "scope": "common", + "docId": "kibSavedObjectsFinderPluginApi", + "section": "def-common.FinderAttributes", + "text": "FinderAttributes" + }, + ">; }) => void; onCreateNew: () => void; }) => JSX.Element" ], "path": "packages/kbn-event-annotation-components/types.ts", "deprecated": false, @@ -508,7 +516,15 @@ "section": "def-common.SavedObjectCommon", "text": "SavedObjectCommon" }, - "; }) => void" + "<", + { + "pluginId": "savedObjectsFinder", + "scope": "common", + "docId": "kibSavedObjectsFinderPluginApi", + "section": "def-common.FinderAttributes", + "text": "FinderAttributes" + }, + ">; }) => void" ], "path": "packages/kbn-event-annotation-components/types.ts", "deprecated": false, @@ -567,13 +583,21 @@ "description": [], "signature": [ { - "pluginId": "@kbn/core-saved-objects-common", + "pluginId": "@kbn/content-management-utils", "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" + "docId": "kibKbnContentManagementUtilsPluginApi", + "section": "def-common.SOWithMetadata", + "text": "SOWithMetadata" }, - "" + "<", + { + "pluginId": "savedObjectsFinder", + "scope": "common", + "docId": "kibSavedObjectsFinderPluginApi", + "section": "def-common.FinderAttributes", + "text": "FinderAttributes" + }, + ">" ], "path": "packages/kbn-event-annotation-components/types.ts", "deprecated": false, @@ -694,27 +718,6 @@ "deprecated": false, "trackAdoption": false, "isRequired": true - }, - { - "parentPluginId": "eventAnnotation", - "id": "def-public.EventAnnotationService.Unnamed.$3", - "type": "Object", - "tags": [], - "label": "savedObjectsManagement", - "description": [], - "signature": [ - { - "pluginId": "savedObjectsManagement", - "scope": "public", - "docId": "kibSavedObjectsManagementPluginApi", - "section": "def-public.SavedObjectsManagementPluginStart", - "text": "SavedObjectsManagementPluginStart" - } - ], - "path": "src/plugins/event_annotation/public/event_annotation_service/index.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true } ], "returnComment": [] diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index f50f62c4a8927..f65e74fe1426a 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 192 | 30 | 192 | 2 | +| 191 | 30 | 191 | 2 | ## Client diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index 6c4dd5d9e564e..c656ca3ece0e6 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -143,85 +143,6 @@ ], "returnComment": [] }, - { - "parentPluginId": "eventLog", - "id": "def-server.ClusterClientAdapter.doesIlmPolicyExist", - "type": "Function", - "tags": [], - "label": "doesIlmPolicyExist", - "description": [], - "signature": [ - "(policyName: string) => Promise" - ], - "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "eventLog", - "id": "def-server.ClusterClientAdapter.doesIlmPolicyExist.$1", - "type": "string", - "tags": [], - "label": "policyName", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "eventLog", - "id": "def-server.ClusterClientAdapter.createIlmPolicy", - "type": "Function", - "tags": [], - "label": "createIlmPolicy", - "description": [], - "signature": [ - "(policyName: string, policy: Record) => Promise" - ], - "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "eventLog", - "id": "def-server.ClusterClientAdapter.createIlmPolicy.$1", - "type": "string", - "tags": [], - "label": "policyName", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "eventLog", - "id": "def-server.ClusterClientAdapter.createIlmPolicy.$2", - "type": "Object", - "tags": [], - "label": "policy", - "description": [], - "signature": [ - "Record" - ], - "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, { "parentPluginId": "eventLog", "id": "def-server.ClusterClientAdapter.doesIndexTemplateExist", diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index e89fac58a2876..e1da7f64405f8 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 116 | 0 | 116 | 11 | +| 111 | 0 | 111 | 11 | ## Server diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 935ec02b807e9..42857923c0755 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index e2041edf19373..fdc2f7061181e 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-08-08 +date: 2023-08-10 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 7939c2f3076e7..c8c6008a375a5 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.devdocs.json b/api_docs/expression_heatmap.devdocs.json index 91fc345d8f6c3..e9ebf4fcbc4fe 100644 --- a/api_docs/expression_heatmap.devdocs.json +++ b/api_docs/expression_heatmap.devdocs.json @@ -897,7 +897,23 @@ "section": "def-common.Datatable", "text": "Datatable" }, - "; column: number; range: number[]; timeFieldName?: string | undefined; }) => void; paletteService: ", + "; column: number; range: number[]; timeFieldName?: string | undefined; }) => void; onClickMultiValue: (data: { data: { table: Pick<", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.Datatable", + "text": "Datatable" + }, + ", \"rows\" | \"columns\">; cells: { column: number; row: number; }[]; relation?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.BooleanRelation", + "text": "BooleanRelation" + }, + " | undefined; }[]; timeFieldName?: string | undefined; negate?: boolean | undefined; }) => void; paletteService: ", { "pluginId": "@kbn/coloring", "scope": "common", diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index c6cf3ed57d316..a16712c92ffe7 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-08-08 +date: 2023-08-10 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 cd90df87cb30a..c99cc4f7ffa55 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-08-08 +date: 2023-08-10 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 40193204df51f..b83b37db8b594 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-08-08 +date: 2023-08-10 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 8456e45c43aa7..ef651f20382d1 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-08-08 +date: 2023-08-10 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 6e1c7fff5b9cb..3ce0102964ccd 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-08-08 +date: 2023-08-10 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 40c1ec352d684..391ae05dfda30 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-08-08 +date: 2023-08-10 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 fdfc5024941dc..d7c9f4b7c3d4d 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-08-08 +date: 2023-08-10 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 f12b98a3ef638..ad4e5aae94dfd 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-08-08 +date: 2023-08-10 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 0b38793ebf1d1..4e6acabab2e13 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-08-08 +date: 2023-08-10 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 fdd5a6e7434a1..954f34351c103 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-08-08 +date: 2023-08-10 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 13291fe251db6..3d3a703604593 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-08-08 +date: 2023-08-10 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 46144bdd70a71..2cfb8da03bab3 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-08-08 +date: 2023-08-10 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 c84e5990a374f..87032206bd767 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-08-08 +date: 2023-08-10 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 d0a6e1861cdcb..eb519823e0afd 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-08-08 +date: 2023-08-10 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 4ad74f547420c..2f98ac1236833 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index c6085d88884ac..e361a3c2a6336 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -577,6 +577,14 @@ "plugin": "cases", "path": "x-pack/plugins/cases/public/plugin.ts" }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/plugin.test.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/plugin.test.ts" + }, { "plugin": "imageEmbeddable", "path": "src/plugins/image_embeddable/public/plugin.ts" @@ -5458,6 +5466,10 @@ { "plugin": "cases", "path": "x-pack/plugins/cases/server/client/factory.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/server/plugin.test.ts" } ] } diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 459bc877566b3..fc314b289752a 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index d7b28cd1617f8..6aa91c16d3b1e 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index a5600ae945a8e..10d05a3e19944 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-08-08 +date: 2023-08-10 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 1bb35a9da4b34..3ecfb4969ccc2 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 649eab3e58dab..7a5a09f7ae7f4 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 37986e0e5531e..f354f40dab1a1 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 84f6acf3fc8e9..33c414a76476c 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index bbac09985808e..30d558400d2a5 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-08-08 +date: 2023-08-10 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 39520e4f5fd12..6f1bd38953fe1 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-08-08 +date: 2023-08-10 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 43b6b1f6d5fec..55b6de533eb18 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-08-08 +date: 2023-08-10 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 f8fb7c62396c0..d48e479972b6c 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-08-08 +date: 2023-08-10 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 f24b897b682f1..7efd702cfa51e 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-08-08 +date: 2023-08-10 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 66b909ccff85d..e941d12298554 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.devdocs.json b/api_docs/kbn_aiops_components.devdocs.json index 1b99ef4188fb6..702a63d266bef 100644 --- a/api_docs/kbn_aiops_components.devdocs.json +++ b/api_docs/kbn_aiops_components.devdocs.json @@ -23,15 +23,13 @@ "parentPluginId": "@kbn/aiops-components", "id": "def-common.DocumentCountChart", "type": "Function", - "tags": [ - "constructor" - ], + "tags": [], "label": "DocumentCountChart", "description": [ - "\nDocument count chart with draggable brushes to select time ranges\nby default use `Baseline` and `Deviation` for the badge names" + "\nDocument count chart with draggable brushes to select time ranges\nby default use `Baseline` and `Deviation` for the badge names\n" ], "signature": [ - "({ dependencies, brushSelectionUpdateHandler, width, chartPoints, chartPointsSplit, timeRangeEarliest, timeRangeLatest, interval, chartPointsSplitLabel, isBrushCleared, autoAnalysisStart, barColorOverride, barStyleAccessor, barHighlightColorOverride, deviationBrush, baselineBrush, }: React.PropsWithChildren<", + "(props: React.PropsWithChildren<", { "pluginId": "@kbn/aiops-components", "scope": "common", @@ -50,8 +48,10 @@ "id": "def-common.DocumentCountChart.$1", "type": "CompoundType", "tags": [], - "label": "{\n dependencies,\n brushSelectionUpdateHandler,\n width,\n chartPoints,\n chartPointsSplit,\n timeRangeEarliest,\n timeRangeLatest,\n interval,\n chartPointsSplitLabel,\n isBrushCleared,\n autoAnalysisStart,\n barColorOverride,\n barStyleAccessor,\n barHighlightColorOverride,\n deviationBrush = {},\n baselineBrush = {},\n}", - "description": [], + "label": "props", + "description": [ + "DocumentCountChart component props" + ], "signature": [ "React.PropsWithChildren<", { @@ -69,22 +69,22 @@ "isRequired": true } ], - "returnComment": [], + "returnComment": [ + "The DocumentCountChart component." + ], "initialIsOpen": false }, { "parentPluginId": "@kbn/aiops-components", "id": "def-common.DualBrush", "type": "Function", - "tags": [ - "type" - ], + "tags": [], "label": "DualBrush", "description": [ - "\nDualBrush React Component\nDual brush component that overlays the document count chart" + "\nDualBrush React Component\nDual brush component that overlays the document count chart\n" ], "signature": [ - "({\n /**\n * Min and max numeric timestamps for the two brushes\n */\n windowParameters,\n /**\n * Min timestamp for x domain\n */\n min,\n /**\n * Max timestamp for x domain\n */\n max,\n /**\n * Callback function whenever the brush changes\n */\n onChange,\n /**\n * Margin left\n */\n marginLeft,\n /**\n * Nearest timestamps to snap to the brushes to\n */\n snapTimestamps,\n /**\n * Width\n */\n width,\n}: DualBrushProps) => JSX.Element" + "(props: React.PropsWithChildren) => JSX.Element" ], "path": "x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx", "deprecated": false, @@ -93,12 +93,14 @@ { "parentPluginId": "@kbn/aiops-components", "id": "def-common.DualBrush.$1", - "type": "Object", + "type": "CompoundType", "tags": [], - "label": "{\n /**\n * Min and max numeric timestamps for the two brushes\n */\n windowParameters,\n /**\n * Min timestamp for x domain\n */\n min,\n /**\n * Max timestamp for x domain\n */\n max,\n /**\n * Callback function whenever the brush changes\n */\n onChange,\n /**\n * Margin left\n */\n marginLeft,\n /**\n * Nearest timestamps to snap to the brushes to\n */\n snapTimestamps,\n /**\n * Width\n */\n width,\n}", - "description": [], + "label": "props", + "description": [ + "DualBrushProps component props" + ], "signature": [ - "DualBrushProps" + "React.PropsWithChildren" ], "path": "x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx", "deprecated": false, @@ -115,15 +117,13 @@ "parentPluginId": "@kbn/aiops-components", "id": "def-common.DualBrushAnnotation", "type": "Function", - "tags": [ - "type" - ], + "tags": [], "label": "DualBrushAnnotation", "description": [ - "\nDualBrushAnnotation React Component\nDual brush annotation component that overlays the document count chart" + "\nDualBrushAnnotation React Component\nDual brush annotation component that overlays the document count chart\n" ], "signature": [ - "({ id, min, max, style }: React.PropsWithChildren) => JSX.Element" + "(props: React.PropsWithChildren) => JSX.Element" ], "path": "x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush_annotation.tsx", "deprecated": false, @@ -134,8 +134,10 @@ "id": "def-common.DualBrushAnnotation.$1", "type": "CompoundType", "tags": [], - "label": "{ id, min, max, style }", - "description": [], + "label": "props", + "description": [ + "BrushAnnotationProps component props" + ], "signature": [ "React.PropsWithChildren" ], @@ -154,15 +156,13 @@ "parentPluginId": "@kbn/aiops-components", "id": "def-common.ProgressControls", "type": "Function", - "tags": [ - "type" - ], + "tags": [], "label": "ProgressControls", "description": [ - "\nProgressControls React Component\nComponent with ability to Run & cancel analysis\nby default use `Baseline` and `Deviation` for the badge name" + "\nProgressControls React Component\nComponent with ability to Run & cancel analysis\nby default uses `Baseline` and `Deviation` for the badge name\n" ], "signature": [ - "({ children, isBrushCleared, progress, progressMessage, onRefresh, onCancel, onReset, isRunning, shouldRerunAnalysis, runAnalysisDisabled, }: React.PropsWithChildren) => JSX.Element" + "(props: React.PropsWithChildren) => JSX.Element" ], "path": "x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx", "deprecated": false, @@ -173,8 +173,10 @@ "id": "def-common.ProgressControls.$1", "type": "CompoundType", "tags": [], - "label": "{\n children,\n isBrushCleared,\n progress,\n progressMessage,\n onRefresh,\n onCancel,\n onReset,\n isRunning,\n shouldRerunAnalysis,\n runAnalysisDisabled = false,\n}", - "description": [], + "label": "props", + "description": [ + "ProgressControls component props" + ], "signature": [ "React.PropsWithChildren" ], @@ -193,12 +195,12 @@ "interfaces": [ { "parentPluginId": "@kbn/aiops-components", - "id": "def-common.DocumentCountChartPoint", + "id": "def-common.BrushSettings", "type": "Interface", "tags": [], - "label": "DocumentCountChartPoint", + "label": "BrushSettings", "description": [ - "\nDatum for the bar chart" + "\nBrush settings" ], "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", "deprecated": false, @@ -206,15 +208,34 @@ "children": [ { "parentPluginId": "@kbn/aiops-components", - "id": "def-common.DocumentCountChartPoint.time", - "type": "CompoundType", + "id": "def-common.BrushSettings.label", + "type": "string", + "tags": [], + "label": "label", + "description": [ + "\nOptional label name for brush" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/aiops-components", + "id": "def-common.BrushSettings.annotationStyle", + "type": "Object", "tags": [], - "label": "time", + "label": "annotationStyle", "description": [ - "\nTime of bucket" + "\nOptional style for brush" ], "signature": [ - "string | number" + "RecursivePartial", + "<", + "RectAnnotationStyle", + "> | undefined" ], "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", "deprecated": false, @@ -222,12 +243,15 @@ }, { "parentPluginId": "@kbn/aiops-components", - "id": "def-common.DocumentCountChartPoint.value", + "id": "def-common.BrushSettings.badgeWidth", "type": "number", "tags": [], - "label": "value", + "label": "badgeWidth", "description": [ - "\nNumber of doc count for that time bucket" + "\nOptional width for brush" + ], + "signature": [ + "number | undefined" ], "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", "deprecated": false, @@ -304,61 +328,21 @@ "tags": [], "label": "brushSelectionUpdateHandler", "description": [ - "Optional callback function which gets called the brush selection has changed" + "Optional callback for handling brush selection updates" ], "signature": [ - "((windowParameters: ", { - "pluginId": "@kbn/aiops-utils", + "pluginId": "@kbn/aiops-components", "scope": "common", - "docId": "kibKbnAiopsUtilsPluginApi", - "section": "def-common.WindowParameters", - "text": "WindowParameters" + "docId": "kibKbnAiopsComponentsPluginApi", + "section": "def-common.BrushSelectionUpdateHandler", + "text": "BrushSelectionUpdateHandler" }, - ", force: boolean) => void) | undefined" + " | undefined" ], "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/aiops-components", - "id": "def-common.DocumentCountChartProps.brushSelectionUpdateHandler.$1", - "type": "Object", - "tags": [], - "label": "windowParameters", - "description": [], - "signature": [ - { - "pluginId": "@kbn/aiops-utils", - "scope": "common", - "docId": "kibKbnAiopsUtilsPluginApi", - "section": "def-common.WindowParameters", - "text": "WindowParameters" - } - ], - "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/aiops-components", - "id": "def-common.DocumentCountChartProps.brushSelectionUpdateHandler.$2", - "type": "boolean", - "tags": [], - "label": "force", - "description": [], - "signature": [ - "boolean" - ], - "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "@kbn/aiops-components", @@ -387,11 +371,11 @@ ], "signature": [ { - "pluginId": "@kbn/aiops-components", + "pluginId": "@kbn/aiops-utils", "scope": "common", - "docId": "kibKbnAiopsComponentsPluginApi", - "section": "def-common.DocumentCountChartPoint", - "text": "DocumentCountChartPoint" + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.LogRateHistogramItem", + "text": "LogRateHistogramItem" }, "[]" ], @@ -410,11 +394,11 @@ ], "signature": [ { - "pluginId": "@kbn/aiops-components", + "pluginId": "@kbn/aiops-utils", "scope": "common", - "docId": "kibKbnAiopsComponentsPluginApi", - "section": "def-common.DocumentCountChartPoint", - "text": "DocumentCountChartPoint" + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.LogRateHistogramItem", + "text": "LogRateHistogramItem" }, "[] | undefined" ], @@ -570,7 +554,13 @@ "Optional settings override for the 'deviation' brush" ], "signature": [ - "BrushSettings", + { + "pluginId": "@kbn/aiops-components", + "scope": "common", + "docId": "kibKbnAiopsComponentsPluginApi", + "section": "def-common.BrushSettings", + "text": "BrushSettings" + }, " | undefined" ], "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", @@ -587,7 +577,13 @@ "Optional settings override for the 'baseline' brush" ], "signature": [ - "BrushSettings", + { + "pluginId": "@kbn/aiops-components", + "scope": "common", + "docId": "kibKbnAiopsComponentsPluginApi", + "section": "def-common.BrushSettings", + "text": "BrushSettings" + }, " | undefined" ], "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", @@ -599,7 +595,95 @@ } ], "enums": [], - "misc": [], + "misc": [ + { + "parentPluginId": "@kbn/aiops-components", + "id": "def-common.BrushSelectionUpdateHandler", + "type": "Type", + "tags": [], + "label": "BrushSelectionUpdateHandler", + "description": [ + "\nCallback function which gets called when the brush selection has changed\n" + ], + "signature": [ + "(windowParameters: ", + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.WindowParameters", + "text": "WindowParameters" + }, + ", force: boolean, logRateAnalysisType: ", + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.LogRateAnalysisType", + "text": "LogRateAnalysisType" + }, + ") => void" + ], + "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/aiops-components", + "id": "def-common.BrushSelectionUpdateHandler.$1", + "type": "Object", + "tags": [], + "label": "windowParameters", + "description": [ + "Baseline and deviation time ranges." + ], + "signature": [ + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.WindowParameters", + "text": "WindowParameters" + } + ], + "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/aiops-components", + "id": "def-common.BrushSelectionUpdateHandler.$2", + "type": "boolean", + "tags": [], + "label": "force", + "description": [ + "Force update" + ], + "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/aiops-components", + "id": "def-common.BrushSelectionUpdateHandler.$3", + "type": "CompoundType", + "tags": [], + "label": "logRateAnalysisType", + "description": [ + "`spike` or `dip` based on median log rate bucket size" + ], + "signature": [ + "\"spike\" | \"dip\"" + ], + "path": "x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], "objects": [] } } \ No newline at end of file diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index e4e3eed672b02..77111d5895f85 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 30 | 0 | 6 | 1 | +| 33 | 0 | 0 | 0 | ## Common @@ -31,3 +31,6 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi ### Interfaces +### Consts, variables and types + + diff --git a/api_docs/kbn_aiops_utils.devdocs.json b/api_docs/kbn_aiops_utils.devdocs.json index c84540e90ecf6..4ab34fcaf1db4 100644 --- a/api_docs/kbn_aiops_utils.devdocs.json +++ b/api_docs/kbn_aiops_utils.devdocs.json @@ -19,6 +19,98 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.getLogRateAnalysisType", + "type": "Function", + "tags": [], + "label": "getLogRateAnalysisType", + "description": [ + "\nIdentify the log rate analysis type based on the baseline/deviation\ntime ranges on a given log rate histogram.\n" + ], + "signature": [ + "(logRateHistogram: ", + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.LogRateHistogramItem", + "text": "LogRateHistogramItem" + }, + "[], windowParameters: ", + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.WindowParameters", + "text": "WindowParameters" + }, + ") => ", + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.LogRateAnalysisType", + "text": "LogRateAnalysisType" + } + ], + "path": "x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.getLogRateAnalysisType.$1", + "type": "Array", + "tags": [], + "label": "logRateHistogram", + "description": [ + "The log rate histogram." + ], + "signature": [ + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.LogRateHistogramItem", + "text": "LogRateHistogramItem" + }, + "[]" + ], + "path": "x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.getLogRateAnalysisType.$2", + "type": "Object", + "tags": [], + "label": "windowParameters", + "description": [ + "The window parameters with baseline and deviation time range." + ], + "signature": [ + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.WindowParameters", + "text": "WindowParameters" + } + ], + "path": "x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "The log rate analysis type." + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/aiops-utils", "id": "def-common.getSnappedWindowParameters", @@ -46,7 +138,7 @@ "text": "WindowParameters" } ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -68,7 +160,7 @@ "text": "WindowParameters" } ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -85,7 +177,7 @@ "signature": [ "number[]" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -115,7 +207,7 @@ "text": "WindowParameters" } ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -131,7 +223,7 @@ "signature": [ "number" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -148,7 +240,7 @@ "signature": [ "number" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -165,7 +257,7 @@ "signature": [ "number" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -178,6 +270,51 @@ } ], "interfaces": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.LogRateHistogramItem", + "type": "Interface", + "tags": [], + "label": "LogRateHistogramItem", + "description": [ + "\nLog rate histogram item" + ], + "path": "x-pack/packages/ml/aiops_utils/log_rate_histogram_item.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.LogRateHistogramItem.time", + "type": "CompoundType", + "tags": [], + "label": "time", + "description": [ + "\nTime of bucket" + ], + "signature": [ + "string | number" + ], + "path": "x-pack/packages/ml/aiops_utils/log_rate_histogram_item.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.LogRateHistogramItem.value", + "type": "number", + "tags": [], + "label": "value", + "description": [ + "\nNumber of doc count for that time bucket" + ], + "path": "x-pack/packages/ml/aiops_utils/log_rate_histogram_item.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/aiops-utils", "id": "def-common.WindowParameters", @@ -191,7 +328,7 @@ "description": [ "\nTime range definition for baseline and deviation to be used by log rate analysis.\n" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -206,7 +343,7 @@ "description": [ "\nBaseline minimum value" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false }, @@ -221,7 +358,7 @@ "description": [ "\nBaseline maximum value" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false }, @@ -236,7 +373,7 @@ "description": [ "\nDeviation minimum value" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false }, @@ -251,7 +388,7 @@ "description": [ "\nDeviation maximum value" ], - "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, "trackAdoption": false } @@ -260,7 +397,43 @@ } ], "enums": [], - "misc": [], - "objects": [] + "misc": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.LogRateAnalysisType", + "type": "Type", + "tags": [], + "label": "LogRateAnalysisType", + "description": [ + "\nUnion type of log rate analysis types." + ], + "signature": [ + "\"spike\" | \"dip\"" + ], + "path": "x-pack/packages/ml/aiops_utils/log_rate_analysis_type.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.LOG_RATE_ANALYSIS_TYPE", + "type": "Object", + "tags": [], + "label": "LOG_RATE_ANALYSIS_TYPE", + "description": [ + "\nThe type of log rate analysis (spike or dip) will affect how parameters are\npassed to the analysis API endpoint." + ], + "signature": [ + "{ readonly SPIKE: \"spike\"; readonly DIP: \"dip\"; }" + ], + "path": "x-pack/packages/ml/aiops_utils/log_rate_analysis_type.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] } } \ No newline at end of file diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 8ab1310b76c47..5abc5d88e118c 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; @@ -21,13 +21,19 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 0 | 0 | +| 20 | 0 | 0 | 0 | ## Common +### Objects + + ### Functions ### Interfaces +### Consts, variables and types + + diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 748be1e48ebf4..418e8b1ba5cbf 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index c7101ea8fa22b..1a2bfdb84ca88 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index d3929a4dea717..6d7a5d4431245 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index f3bf145144bb5..0696cb1f1333a 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index 88e2702e0c82f..1af5ff6be1368 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -1316,14 +1316,14 @@ "plugin": "cloud", "path": "x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts" }, - { - "plugin": "telemetry", - "path": "src/plugins/telemetry/server/plugin.ts" - }, { "plugin": "security", "path": "x-pack/plugins/security/public/analytics/register_user_context.ts" }, + { + "plugin": "telemetry", + "path": "src/plugins/telemetry/server/plugin.ts" + }, { "plugin": "telemetry", "path": "src/plugins/telemetry/public/plugin.ts" diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 0dec0bae902c6..32f4324c0b45f 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-08-08 +date: 2023-08-10 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 ad3639f903acd..a338b46b94f30 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 579d8f9c06d06..1421c801f0f97 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-08-08 +date: 2023-08-10 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 ea346f0f543ae..ad83c7e663383 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-08-08 +date: 2023-08-10 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 32ea46da84ad8..1d88dbb364a46 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index 534625a620b5a..5f1c1de0ad4cc 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 19b3c53dc1fa6..12df92a5bdf59 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 9082cac1d53cf..38658ba9378bf 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 0793199486530..e2a9b389ca3cd 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 35fd8b188e4bb..c91252fd33610 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-08-08 +date: 2023-08-10 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 8796a2577f2e8..a022b36191609 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 60115e5095ec3..5c3a636ae44af 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index f1ea4d738f4e6..3766d6c35e77e 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index 965afdd0dd7b4..c5ea02975d07e 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index e23a54e6cfde5..dd478068217b3 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-08-08 +date: 2023-08-10 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 bcdaad75b0888..88633fead7a31 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-08-08 +date: 2023-08-10 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 c9afcf2d42fe4..da44070bf0374 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-08-08 +date: 2023-08-10 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 3db876a7ee320..ba30db316a708 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-08-08 +date: 2023-08-10 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 f05ae46a3609a..0db9769db14d3 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 8bf30ebb4b9d0..65373570a0e98 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index b8bf6bc9537dd..db828f46c981b 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 33fd7a4d6418b..fb50d2bb20a3f 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-08-08 +date: 2023-08-10 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 b74fada882cdd..5c12a2fc8086f 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-08-08 +date: 2023-08-10 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 3524e6e8d82aa..df5e89ed1a7ce 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 71e8e4fea5a34..33ec97ec50893 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 3db5874410372..44f11f4a4dda3 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index 74fe7e277a2ec..e68db52cba0d5 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index 0a393870c35cf..cd03b964fe93e 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 2560ce1238725..08e9fe08765a3 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.devdocs.json b/api_docs/kbn_content_management_utils.devdocs.json index e58246c889b47..871287406cc8f 100644 --- a/api_docs/kbn_content_management_utils.devdocs.json +++ b/api_docs/kbn_content_management_utils.devdocs.json @@ -74,7 +74,7 @@ "id": "def-common.SOContentStorage.Unnamed.$1", "type": "Object", "tags": [], - "label": "{\n savedObjectType,\n cmServicesDefinition,\n createArgsToSoCreateOptions,\n updateArgsToSoUpdateOptions,\n searchArgsToSOFindOptions,\n enableMSearch,\n allowedSavedObjectAttributes,\n }", + "label": "{\n savedObjectType,\n cmServicesDefinition,\n createArgsToSoCreateOptions,\n updateArgsToSoUpdateOptions,\n searchArgsToSOFindOptions,\n enableMSearch,\n allowedSavedObjectAttributes,\n mSearchAdditionalSearchFields,\n }", "description": [], "signature": [ { @@ -118,7 +118,7 @@ "section": "def-common.SavedObjectsFindResult", "text": "SavedObjectsFindResult" }, - ") => Types[\"Item\"]; } | undefined" + ") => Types[\"Item\"]; additionalSearchFields?: string[] | undefined; } | undefined" ], "path": "packages/kbn-content-management-utils/src/saved_object_content_storage.ts", "deprecated": false, @@ -2865,6 +2865,20 @@ "path": "packages/kbn-content-management-utils/src/saved_object_content_storage.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/content-management-utils", + "id": "def-common.SOContentStorageConstrutorParams.mSearchAdditionalSearchFields", + "type": "Array", + "tags": [], + "label": "mSearchAdditionalSearchFields", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-content-management-utils/src/saved_object_content_storage.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 4b8b9829a1a50..55af16f30d880 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 187 | 1 | 122 | 0 | +| 188 | 1 | 123 | 0 | ## Common diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 54b1c35b9669f..a3d2aa0c4415e 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 56dacaceda3e6..7e25cbeda0a88 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 f0bbbf320541d..7caeee42494af 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 cb51ca86bda86..9fc02fe58fa4f 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-08-08 +date: 2023-08-10 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 03048cb389098..ca9f1516bec86 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 8d63466db6756..bc0795bf6158f 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 90fab26c3bf54..2c0d6086d55a3 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 e5c429b13bc13..d0a5218b0ceae 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 025d1bd9e42cb..a9b59dae2cdc3 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 253dcb5d92671..dbd536efd4af8 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index b305c86fe1c93..28432533e8282 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 7c776934de2fc..8bc2bb6c0354d 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 284e58f106333..c3cdc773148c1 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index c017c2b09c5fc..3c4070c221909 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 730462673de14..ff57bebc5c3a5 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-08-08 +date: 2023-08-10 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 cf5ab5cb40877..981ca2d333281 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 07cbfbfb0af7e..b417fb5efe040 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 98eedb888c16a..8d9dd57cfc8bc 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 8effd8f79e6e2..cb5c8d84690da 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-08-08 +date: 2023-08-10 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 133401c40993b..37895f8d77f79 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-08-08 +date: 2023-08-10 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 7b1538003a8be..49fed84525600 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 7128a9710b907..2ead6c497949c 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 228624d0ecaf2..437ee4fdd75fc 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 4251ce1a56b61..ead55725566be 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 670995a4a05c8..e84f5576a9ec7 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 35e42e0ea93dc..b37f87902aff2 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 88779b741c0f6..1cff1fd02a433 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 11415cb9894d6..53e46ff803c2b 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index c6cef5d82d103..d94fb747f4731 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index d021591eff678..ee237e02ad449 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 68c2414844881..0ff4dc99b1ca1 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 947af0a1b0da4..a2f9efd771efe 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 f94179345c264..b178fb5e76a4e 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 1d15fcc40b881..4b2a04a993b7d 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 582190d70f7b8..fca4609153d74 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-08-08 +date: 2023-08-10 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 5be7399218312..646787175c940 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-08-08 +date: 2023-08-10 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 89421e8bdb148..f2c041d63f34b 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 f25e1c8a0ee66..61ffa6dd89dc4 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 b81abcc8258de..783da1f326902 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 735b84c1b0079..9ca5d0b80a63b 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 67eedbc41794e..4f9fa2f6d4e2b 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-08-08 +date: 2023-08-10 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 b839841f35de9..7df248651d145 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 686e5e9661d4c..34d5df5e1d5ca 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 24e3759ce4b8a..8a374a174da77 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 dadbf28ea6332..48bc60defbb9d 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-08-08 +date: 2023-08-10 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 e903616263fb4..d872809f9fbcd 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 8fe13951442ca..769af6390cbb8 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 1256a5eb245e7..78956563fd545 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 04ea7169eb666..ec60e78f18e7c 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 90009c29c89cc..ea02ea43a5abd 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 7716f62714f6b..62a7277f4da84 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 f4c2a8115015b..6a38822734be7 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 cb08d0340717d..be9d12fb509e6 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-08-08 +date: 2023-08-10 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 321b72ad4e2f5..9cf6a0c2945aa 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-08-08 +date: 2023-08-10 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 78bcb9afedc40..6b9c1e9531847 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 d6aa6ab279e28..c51c1c80290ff 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 19aa36087e3fd..16641ee0f9618 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 1d63ca16b335c..7203a607e4f16 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 787454514e00e..9837e89fcd2bb 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 87e0876cc677d..a5b7c23ba23f6 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 57abe05b1e9a3..9ca62691cf917 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 6d0f2c07a925c..81fc4f5ea6bf9 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-08-08 +date: 2023-08-10 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 8ee889cbcf8ad..05a1daf171451 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index 3d3161cb7cbb3..faaac088132f2 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index fa1fa17e20bb6..b7a801fa451e8 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 636c73bcfe78f..6a55ec423723a 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index c63fcf2523f89..6b1d80bbaa14b 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 45554c9823289..1e222721df01d 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 a9c57abc605a1..0aa09c0f91cdf 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index 45aa782bcbd31..82728a75430e6 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -3599,18 +3599,6 @@ "plugin": "alerting", "path": "x-pack/plugins/alerting/server/routes/maintenance_window/active_maintenance_windows.ts" }, - { - "plugin": "guidedOnboarding", - "path": "src/plugins/guided_onboarding/server/routes/guide_state_routes.ts" - }, - { - "plugin": "guidedOnboarding", - "path": "src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts" - }, - { - "plugin": "guidedOnboarding", - "path": "src/plugins/guided_onboarding/server/routes/config_routes.ts" - }, { "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts" @@ -3631,6 +3619,18 @@ "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/routes/get_aad_fields_by_rule_type.ts" }, + { + "plugin": "guidedOnboarding", + "path": "src/plugins/guided_onboarding/server/routes/guide_state_routes.ts" + }, + { + "plugin": "guidedOnboarding", + "path": "src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts" + }, + { + "plugin": "guidedOnboarding", + "path": "src/plugins/guided_onboarding/server/routes/config_routes.ts" + }, { "plugin": "observability", "path": "x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts" @@ -4687,10 +4687,6 @@ "plugin": "interactiveSetup", "path": "src/plugins/interactive_setup/server/routes/status.ts" }, - { - "plugin": "savedObjectsFinder", - "path": "src/plugins/saved_objects_finder/server/routes/find.ts" - }, { "plugin": "savedObjectsManagement", "path": "src/plugins/saved_objects_management/server/routes/find.ts" @@ -13778,10 +13774,6 @@ "plugin": "@kbn/core-http-router-server-mocks", "path": "packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts" }, - { - "plugin": "bfetch", - "path": "src/plugins/bfetch/server/plugin.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/public/runtime_fields/get_runtime_field.ts" @@ -13814,6 +13806,10 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/internal/has_data_views.ts" }, + { + "plugin": "bfetch", + "path": "src/plugins/bfetch/server/plugin.ts" + }, { "plugin": "data", "path": "src/plugins/data/server/search/routes/session.ts" @@ -14042,6 +14038,10 @@ "plugin": "ml", "path": "x-pack/plugins/ml/server/routes/trained_models.ts" }, + { + "plugin": "ml", + "path": "x-pack/plugins/ml/server/routes/trained_models.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/routes/management.ts" @@ -14510,10 +14510,6 @@ "plugin": "@kbn/core-http-router-server-mocks", "path": "packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts" }, - { - "plugin": "bfetch", - "path": "src/plugins/bfetch/server/plugin.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/public/runtime_fields/put_runtime_field.ts" @@ -14526,6 +14522,10 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts" }, + { + "plugin": "bfetch", + "path": "src/plugins/bfetch/server/plugin.ts" + }, { "plugin": "data", "path": "src/plugins/data/server/search/routes/session.ts" @@ -14698,10 +14698,6 @@ "plugin": "@kbn/core-http-router-server-mocks", "path": "packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts" }, - { - "plugin": "bfetch", - "path": "src/plugins/bfetch/server/plugin.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/public/fields/update_fields.ts" @@ -14742,6 +14738,10 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts" }, + { + "plugin": "bfetch", + "path": "src/plugins/bfetch/server/plugin.ts" + }, { "plugin": "data", "path": "src/plugins/data/server/search/routes/session.ts" @@ -14778,6 +14778,10 @@ "plugin": "data", "path": "src/plugins/data/server/kql_telemetry/route.ts" }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/server/autocomplete/value_suggestions_route.ts" + }, { "plugin": "aiops", "path": "x-pack/plugins/aiops/server/routes/log_rate_analysis.ts" @@ -14786,10 +14790,6 @@ "plugin": "aiops", "path": "x-pack/plugins/aiops/server/routes/log_categorization.ts" }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/server/autocomplete/value_suggestions_route.ts" - }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/routes/annotations.ts" @@ -15074,6 +15074,10 @@ "plugin": "ml", "path": "x-pack/plugins/ml/server/routes/trained_models.ts" }, + { + "plugin": "ml", + "path": "x-pack/plugins/ml/server/routes/trained_models.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/routes/alerting.ts" @@ -15418,10 +15422,6 @@ "plugin": "@kbn/core-http-router-server-mocks", "path": "packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts" }, - { - "plugin": "bfetch", - "path": "src/plugins/bfetch/server/plugin.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/public/runtime_fields/delete_runtime_field.ts" @@ -15434,6 +15434,10 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/rest_api_routes/public/delete_data_view.ts" }, + { + "plugin": "bfetch", + "path": "src/plugins/bfetch/server/plugin.ts" + }, { "plugin": "data", "path": "src/plugins/data/server/search/routes/session.ts" diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 04b45513b45c5..99d0bfed889dd 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-08-08 +date: 2023-08-10 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 df28c49f0862d..8cb45c96afba6 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 71fd4602c628d..fae37a80af603 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 dedebc31e31fd..4d5eb5471bd82 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 d196e9513ab6c..0253dae1815ba 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 a4f118dfa303d..222e7c93daf9e 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-08-08 +date: 2023-08-10 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 1c1d80320475a..1c5e29a1b819b 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 5f6215c2b0e27..22aebb96c496a 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 5391d2f281be5..037cc811c3a54 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 df5e3bb4349d7..c29d40f5f7313 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 56086553b00dc..e6ba4de3c1fb1 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 99531b81e8574..aa5e529950a8a 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index 2a0f438edb26b..5587dcb2c4b83 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index a02bdb3c0101e..954d92db64bf5 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index 7d2581f42e4f6..d9dfcd3237c55 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index ba13559eba6bf..908787f976911 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 29169fb4371cd..bbadb69f2a581 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 123e3aa3e7619..703169e0049ca 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-08-08 +date: 2023-08-10 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 4fb6a77b9b04d..575f90597d2d3 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 3d86ef8947788..94453374d508a 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 dc7e196ccdf06..1203a3ea18a91 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 317ba2ee9e774..964eada8d67a9 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 ace69d608a26d..65b165872eab4 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-08-08 +date: 2023-08-10 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 751b14ef4a567..6b5f2599ab21e 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 b8bc5cfc50f5b..b8075163f5f03 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 f558d06a364d1..4a47840063206 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 010eab5713a79..4fee677a2e341 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-08-08 +date: 2023-08-10 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 b142c9f37285f..a0c126ef3ff94 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 6c94ef3afbb40..ed03155a5ed93 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 c2638a4737c23..326dc303a3b1c 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 0e95009b0cb85..58a476462aba0 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 a25e35d4e19ba..b09dceee0054e 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 69b5674fe89ab..791e5bb452ac0 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 079aed26d91d7..536239c5f598a 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 b39ee812ab659..a0fd9d020d751 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 916c6fafc8bd0..4ecac68faabf8 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index a6f60b7e0e965..212070fdb5bca 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index c2a5349922e0d..e6594e6507489 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 18f595bde6978..6b3fc9263cdbf 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 78e0717918e17..3234e959b51c7 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-08-08 +date: 2023-08-10 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 86670bcafb4ae..5b503dd6ca04f 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 6aec9dfd84597..8c0c299eb62d8 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index abb1fb3e8afe6..67ade810a2659 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index ff0bc3ad493e7..e658945d42019 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index b38e9d98875bb..7b2c53aaa9659 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index a62569321db1f..43e10686ceb1a 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 b4501b269354e..b23c95b79201f 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-08-08 +date: 2023-08-10 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_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 95fa85cab16b5..a7a431da29a06 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 d1e46f4c1ee5e..630637ff9c9c1 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 a987dc60dc8d8..03eda68dd88f5 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 c493c59bc1b4a..73189853b8392 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 d6b01343a214b..ad0b541c70c20 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 99a7f0976ded9..e8a44056f0121 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 7d5ba1632a8d4..94bb7cb8e1b23 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-08-08 +date: 2023-08-10 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 758ebdab3d222..2df414cb3785c 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 99235c2ce242a..2b80851350953 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 845cab1c1d84b..f67a0413da870 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 d8ae52e1c5cd1..46ff072146b36 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 d54b0c18fa7f9..f9fdbea16f255 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-08-08 +date: 2023-08-10 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 48c029ae6a3fa..2eff7e0f56a05 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 7b0108f47de19..c4449475b455a 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 4cd54142fc9d2..d6644b87e6e2c 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index f1c9d87eab6cf..c0517668bd3e6 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index 1d759a21ce701..f1cbd7a5d8f57 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index e654990475f5d..8a5f1c295ab8b 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 117678763edcb..20f2eed5b139b 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 6695ef9c86476..43033b1972ce6 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 271a06dbd26e7..a4dcb69ed3fcb 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-08-08 +date: 2023-08-10 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 1bf1a38c714f5..0517bdd2d1419 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 56b267ba84572..62dd992d0d761 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index bc776395968b5..7194e3044a2a7 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index c6feaf4650780..c152eb2c4ed2d 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index cdbe74136748f..4c82c21c3a216 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 9ddf2c57ce822..a5c94c3afbba7 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 7abf2e74bf987..d394eec62e530 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-08-08 +date: 2023-08-10 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 1931ac3b26fff..8206d774761f4 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 532c7adad3e1c..c70b178a72abc 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 0ab8e28aa0df9..e509dac371cfa 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 7a75e89ea4150..1efa419d1f86f 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index c67e252addd2f..5d9a8b6c67582 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 4e6b67b9852b3..36f34c6b24b90 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index d4cbd84c11c56..c5f0b02883e09 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-08-08 +date: 2023-08-10 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 79c868879899d..41b6f57546a9b 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 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 25757579a4fbd..3421fa3113701 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 935179f08e439..88521aaf6d709 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_internal.mdx b/api_docs/kbn_core_user_settings_server_internal.mdx index dddf1ef343884..3a11aeb9c3c0c 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-internal'] --- import kbnCoreUserSettingsServerInternalObj from './kbn_core_user_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 45056ff08f1af..287c621493ea6 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index cc016eec8bfc8..72ddc4913f2ac 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-08-08 +date: 2023-08-10 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 56ce772d5b1d1..78daead4149fe 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 4c969ab0e95fa..d06760255e077 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index b53458717153d..97f28f5ce3d72 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 4a9f76a2bf852..196e1c6c87297 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 395daccf034c6..0495d60d6de22 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index 4f65a2c1d09d1..67a71ba210d32 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 1517e4387cf9b..2de85cf9a9346 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index 3d1bc0ada81d1..9663d229135ae 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index 1ff762c760aa7..9e3b1781e5d4b 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index e3d1e559f3f0d..d8666f439297d 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 544a84a94c069..0f019717f1a3e 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index 20667b6757a8d..4166cd3a51e6a 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 7c20d850bdd3d..f5b98d82c200b 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 332dab42ea5ec..7e7c53bfb029a 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 08a9a3fb7df56..b859b6745a213 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-08-08 +date: 2023-08-10 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 337ba8695482a..c5f9514bc0c65 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-08-08 +date: 2023-08-10 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 7558dc4339d3f..853a7dd951ca2 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-08-08 +date: 2023-08-10 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 89db217814781..3d176057d5edd 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index b4fad8a137581..e336c9f6f8a6a 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 3e8d6352e66f7..e21a5090c6edb 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -493,7 +493,7 @@ "label": "upgradeAssistant", "description": [], "signature": [ - "{ readonly overview: string; readonly batchReindex: string; readonly remoteReindex: string; }" + "{ readonly overview: string; readonly batchReindex: string; readonly remoteReindex: string; readonly reindexWithPipeline: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 5939e5a135fa4..d9075a7e7934a 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-08-08 +date: 2023-08-10 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 06fbb1b04042e..c90eb47fc8ebf 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 9423dfb94366c..ac85c4ff798d8 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 9fa55673ed2b0..18cdd3e94731b 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 7ba02853d106f..1fd2511549f56 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 0dbcabd54280f..e8184cd642a77 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index 3d27411f79c8a..55c35b7e146ee 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 265c4b5b4b2e5..5050f95627ecc 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 06d97ea2e6c09..c96bdbd286d34 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-08-08 +date: 2023-08-10 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 d6cef59c85df9..6b88702a88dc1 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-08-08 +date: 2023-08-10 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 b152de1a0efad..07413ad66ef3a 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index f5bfbca78eb95..ed6d8ead4aa3a 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index cda3ff57e110d..e5d1042aec594 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 4ce21cb4603ce..2498132adc226 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.devdocs.json b/api_docs/kbn_event_annotation_components.devdocs.json index e835390ba84ac..54b9e95eb01e1 100644 --- a/api_docs/kbn_event_annotation_components.devdocs.json +++ b/api_docs/kbn_event_annotation_components.devdocs.json @@ -1131,7 +1131,15 @@ "section": "def-common.SavedObjectCommon", "text": "SavedObjectCommon" }, - "; }) => void; onCreateNew: () => void; }) => JSX.Element" + "<", + { + "pluginId": "savedObjectsFinder", + "scope": "common", + "docId": "kibSavedObjectsFinderPluginApi", + "section": "def-common.FinderAttributes", + "text": "FinderAttributes" + }, + ">; }) => void; onCreateNew: () => void; }) => JSX.Element" ], "path": "packages/kbn-event-annotation-components/types.ts", "deprecated": false, @@ -1178,7 +1186,15 @@ "section": "def-common.SavedObjectCommon", "text": "SavedObjectCommon" }, - "; }) => void" + "<", + { + "pluginId": "savedObjectsFinder", + "scope": "common", + "docId": "kibSavedObjectsFinderPluginApi", + "section": "def-common.FinderAttributes", + "text": "FinderAttributes" + }, + ">; }) => void" ], "path": "packages/kbn-event-annotation-components/types.ts", "deprecated": false, @@ -1237,13 +1253,21 @@ "description": [], "signature": [ { - "pluginId": "@kbn/core-saved-objects-common", + "pluginId": "@kbn/content-management-utils", + "scope": "common", + "docId": "kibKbnContentManagementUtilsPluginApi", + "section": "def-common.SOWithMetadata", + "text": "SOWithMetadata" + }, + "<", + { + "pluginId": "savedObjectsFinder", "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" + "docId": "kibSavedObjectsFinderPluginApi", + "section": "def-common.FinderAttributes", + "text": "FinderAttributes" }, - "" + ">" ], "path": "packages/kbn-event-annotation-components/types.ts", "deprecated": false, diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index 9cd8f7214cfa8..02b3d4b1a216d 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 732812f7a79b7..365b3085bd27c 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 5bdbfb13a0efb..6b93bad46d751 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-08-08 +date: 2023-08-10 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 2c8eb05ed00fb..3d3c6614c75f7 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index dcd7828d8f729..f95efa21d1831 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 5ab4aeed0523f..0e46c21e56371 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index a40025b07062d..ebf4aa2ab79ff 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 195dc456047cf..d64302ecba7d3 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx index de8d08099659f..02d760595f265 100644 --- a/api_docs/kbn_generate_csv_types.mdx +++ b/api_docs/kbn_generate_csv_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv-types title: "@kbn/generate-csv-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv-types plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] --- import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index e03dc06db9fa2..101b5f981a365 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 7e1396bcb3633..9ad0a5673680d 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-08-08 +date: 2023-08-10 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 fe31787dee3e4..4886cf2557874 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index aa9e3fd4aeea1..3802212b8eb14 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 5f08767456ce9..3051660f3aede 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-08-08 +date: 2023-08-10 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 c58cdd56cebd3..10a295a760f74 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-08-08 +date: 2023-08-10 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 2f53834a6ea42..51da5c5b5baff 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 5f867be9c59b2..23ca2e22cc6b8 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index abce71a506637..3ce1d23d09c4b 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 20c0060b80444..3244c174af0a6 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 769d75499448a..29ec1aaa8c593 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.devdocs.json b/api_docs/kbn_io_ts_utils.devdocs.json index 88c594ba2f1bc..06b7c2f9c5137 100644 --- a/api_docs/kbn_io_ts_utils.devdocs.json +++ b/api_docs/kbn_io_ts_utils.devdocs.json @@ -996,7 +996,7 @@ "description": [], "signature": [ "Type", - "" + "" ], "path": "packages/kbn-io-ts-utils/src/to_boolean_rt/index.ts", "deprecated": false, diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 7871742ad90e1..6c7ec1976753a 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-08-08 +date: 2023-08-10 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 5275a27b9dc42..367e2690e7bb1 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index ba7fc5802fd43..a98049fb4dd26 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 9d53c0f40410b..67e9b1ddef60f 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index c81f36b2dd300..72ff23c059432 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index d55332c57262e..71799546b4e99 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 30e81fe7e6dce..85d66708cb65e 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-08-08 +date: 2023-08-10 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 23defe077087c..d85fa5685be5e 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 f2c347ff0d8e2..6e5ff13b5fb25 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 7b2f34834dc06..0ea3f4156b07c 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index e1ee8679a1f9e..7135df80a0250 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index fe91a56bfe951..adf5e16447694 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index 6c99005c7825a..25ea6d1ddbf37 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 27d6a1535e2cd..9ab81cda021b0 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index e80999e46e2cc..3fd56d85c061c 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 2f32314397b45..9450e44a5d613 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 046a864c1ba4a..1bec7154b1fac 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 55bb1c3e001a9..4f5721186f436 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 194920b61337e..8200185dd676d 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index 2332cec54f230..6887f9dfa1e00 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index b863f9b37db68..4be90bea3c796 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 6807a03355675..ce2db9a911c85 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 495dd2e996efb..11e1c61046bbf 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index ca1444af45137..b9b1bcc1d9b1c 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 3b1ab520333fd..33fe3a77c0241 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index d000fb610f36c..f7621acc959af 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index 69e93bf55bcb8..015eba349a723 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index dc3aa4b33ce9c..f38f1b19de1e9 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 70d162bde8b08..f1c63620a1d4c 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index ffd5f8a5881e1..f5304553a8aa4 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index ff84ab0ed20d7..92506805309af 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index 5086d30b3eb5d..ce9a687d1f9d1 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 42c0c41ca1e7f..9e709e52bd482 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 41c37b3de7060..16a18c689ad81 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 358eadfb73659..9d3a0ad27403d 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 64b83da073a33..2a4a66ba5623d 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 4d20d05ca243f..a4579d6d21b55 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 894950a4c61ef..d2bbc5d60df94 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 6791b1d97fe5a..039fd169e81d3 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-08-08 +date: 2023-08-10 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 85bf9046af977..68f6c2b8ba3ca 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index bda4fcd016615..04f327032e7da 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 051f8d90787bf..3c87b9c0a1a81 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-08-08 +date: 2023-08-10 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 9efe6b300e30b..ce0d0e970141d 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-08-08 +date: 2023-08-10 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 431a8af99adf6..2daac55d98b5f 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 289486f09d428..106e8eb4f681d 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index da5d240964584..c01c73532ed54 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index b23eb4928089b..e964b0faaaa73 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 1af5802080806..36f24195bb524 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index ac75bb3bdb311..1f5954d8df2ab 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 957713bb1a751..d3dbc3f7848bf 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index e2aa061cb4bea..e134d71dba66e 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index c7d6c27548c55..18336f8c9acc2 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index b9c9725d04ee6..740a64be48407 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 7392e055177b3..3cdeed510f331 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index 62955e96811da..2dcf054899ac7 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index d7bab19e316ed..1ec1665d982a0 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index f2dcf6582a1a4..6d8bf92b0fd85 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index d39dc1c0385ae..9ae13dad59ab1 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index aa118bc496e35..473185e7ec57d 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 92409aa81aa38..4d410fa27c46a 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index bb0bc3382f502..6600a72923ae6 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 815a1197552d0..0a5fdef30287b 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 0a74cefaf140a..b1a2f5e29171e 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index ae4497a82d076..b1b81584c877c 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 8726c38268640..db72e41129b27 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 235ece6579de9..998cd2d31a6b7 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 5589b2f530f97..6fb69d797146b 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index c629a70eaeae3..1101bf7bed18e 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 8784dae4ef7d8..04c11e6ab92b8 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index 1435f7d61f57f..166de05b4763a 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 32ca05053289c..cb48b87491d7b 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-08-08 +date: 2023-08-10 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 22a450072fd3e..d251ce0237e69 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-08-08 +date: 2023-08-10 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 ebab6071e8d81..ae09f11f1fbae 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-08-08 +date: 2023-08-10 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 87a6fd81b10ea..091260a1e4bcf 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-08-08 +date: 2023-08-10 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 e87c29be26fe1..5488a4d058e5b 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-08-08 +date: 2023-08-10 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 8d9f9842dc69f..fbba031701e5b 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-08-08 +date: 2023-08-10 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 df6f0c1128ce5..850468aeafd84 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-08-08 +date: 2023-08-10 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 c9430b91c1899..5605d23108058 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-08-08 +date: 2023-08-10 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 7f4b2d4750f03..7b96c67b43423 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-08-08 +date: 2023-08-10 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 650998ba6f321..329ecfa0fe9d9 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-08-08 +date: 2023-08-10 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 6108a52fc0879..1c8bc83eaa5c5 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-08-08 +date: 2023-08-10 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 047a13ff0e0fd..b07ecb81aaaf1 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-08-08 +date: 2023-08-10 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 88150273d7da5..76af845693f60 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-08-08 +date: 2023-08-10 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 f6196c49f2940..34f94831f29e5 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 6f280358cec5c..08d4986085710 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index 9a18e91b6ff78..58a79277bd948 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index f5a753bc3aaab..fbd77da2f7341 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index 41d395bf7e246..a193a2e99e497 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 4beb3231db452..9545b150e3749 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 919291ee2d24d..c7c3d427a869a 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index 31f67a7732224..a22979f5b13c8 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 c7be5ae0d8d23..398025a683674 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-08-08 +date: 2023-08-10 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 4923afb2f3563..a8b7cd7387f1c 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-08-08 +date: 2023-08-10 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 759df6f700995..37b6b3c8648bf 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index 8623b7e4f4d27..f99775fb830e6 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 079cd7d6a5dcb..823cf69e0db54 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 811357a139728..d6e4a179d27a9 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 47814b8c7eb51..991285d262c13 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 91388199fbe63..cae2eb79f4ca1 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index edddbc6f20c0b..fdb550af1e08b 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index 4cc3a68c3d1db..857d7a7f2b9aa 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index b521456e62f20..cb23e3c4bbdd5 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index e9b286aa5109f..6622922486287 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 6451b59819e84..0dbcf32a88f15 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index e7b3f47baf8e7..98ec4209f7b67 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 21e9e7cf31995..14c9140cf23bc 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 71229302d7c5b..7170a26c7c0f3 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 90f4bd8dcad84..31b7a060b7fd4 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-08-08 +date: 2023-08-10 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 6bb75d60c122f..fdebed3eae57d 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 5578bc12670a5..8404a52eeaabb 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-08-08 +date: 2023-08-10 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 7a25309d4728d..8bf4897074ffd 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 561b6e180acac..e5715200637da 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-08-08 +date: 2023-08-10 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 6b8591b832ca7..ac2d30496dcee 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 77ff7acea5927..27d2cd32d78bf 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-08-08 +date: 2023-08-10 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 a84303e13216c..3e68934c3f1c0 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-08-08 +date: 2023-08-10 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 0ea4839401c00..9f80c59c10f1d 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 46a518352d9a9..16c403fdc4687 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-08-08 +date: 2023-08-10 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 c3b6c74213720..a4a60ea678e59 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-08-08 +date: 2023-08-10 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 e3edf8a67b8ea..3a5bfb3535ccf 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-08-08 +date: 2023-08-10 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 42af8a9811ca5..64dc173a2b377 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index d5707fbb5792e..0a01bc802c924 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 65d2a81e0e5b3..cd222a033e70f 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 5964795eb49d4..7c90908187da5 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 74cbfba121c9c..598a397c9a7d2 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-08-08 +date: 2023-08-10 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 8d6ff71719424..96c4501f84f30 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-08-08 +date: 2023-08-10 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 ef0fe567eb189..0fbb5c73c78ab 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 213dffb218e96..970a213cc8643 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index ec092e1d0296d..743b1866288fa 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index c680cf30e2b20..19a122703a83d 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-08-08 +date: 2023-08-10 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 5d8ec59ecb184..cfe4fd52ff873 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-08-08 +date: 2023-08-10 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 755ee9f447cd9..da3af1449decf 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-08-08 +date: 2023-08-10 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 a046a972fb1af..358ccda31ce17 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-08-08 +date: 2023-08-10 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 740c3f77368e1..f9dae23c767a7 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-08-08 +date: 2023-08-10 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 67378b52f35ad..1d99d57a481b2 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index c520055faac88..4bbb77543c64f 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index da6967718d10a..c803989e4246d 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 4fbb7781ad46f..de2fdce4ded2d 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 2c6610824c6e8..b81eb3f6eb438 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index d47a99ee54ce2..6248b21ab9ba7 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 35e03a3ff3065..4b4187bf32ed9 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 8407eb4258544..7914f81c134df 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 6cb41dc8c4f9c..96f6401319ff4 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 3b0bd687483b8..6df817423e527 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx index 17cf7043cdaf1..52be8790127a8 100644 --- a/api_docs/kbn_url_state.mdx +++ b/api_docs/kbn_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-url-state title: "@kbn/url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/url-state plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] --- import kbnUrlStateObj from './kbn_url_state.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.devdocs.json b/api_docs/kbn_user_profile_components.devdocs.json index 73f567df595ef..6082e35d46bb9 100644 --- a/api_docs/kbn_user_profile_components.devdocs.json +++ b/api_docs/kbn_user_profile_components.devdocs.json @@ -172,6 +172,57 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaProvider", + "type": "Function", + "tags": [], + "label": "UserProfilesKibanaProvider", + "description": [ + "\nKibana-specific Provider that maps to known dependency types." + ], + "signature": [ + "({ children, ...services }: React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesKibanaDependencies", + "text": "UserProfilesKibanaDependencies" + }, + ">) => JSX.Element" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaProvider.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n children,\n ...services\n}", + "description": [], + "signature": [ + "React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesKibanaDependencies", + "text": "UserProfilesKibanaDependencies" + }, + ">" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/user-profile-components", "id": "def-common.UserProfilesPopover", @@ -230,6 +281,45 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesProvider", + "type": "Function", + "tags": [], + "label": "UserProfilesProvider", + "description": [ + "\nAbstract external service Provider." + ], + "signature": [ + "({ children, ...services }: React.PropsWithChildren<", + "Services", + ">) => JSX.Element" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesProvider.$1", + "type": "CompoundType", + "tags": [], + "label": "{ children, ...services }", + "description": [], + "signature": [ + "React.PropsWithChildren<", + "Services", + ">" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/user-profile-components", "id": "def-common.UserProfilesSelectable", @@ -338,6 +428,55 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.useUpdateUserProfile", + "type": "Function", + "tags": [], + "label": "useUpdateUserProfile", + "description": [], + "signature": [ + "({ notificationSuccess, pageReloadChecker, }?: Props) => { update: (updatedData: D) => Promise; showSuccessNotification: ({ isRefreshRequired }?: { isRefreshRequired?: boolean | undefined; }) => void; userProfileData: ", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileData", + "text": "UserProfileData" + }, + " | null | undefined; isLoading: boolean; }" + ], + "path": "packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.useUpdateUserProfile.$1", + "type": "Object", + "tags": [], + "label": "{\n notificationSuccess = {},\n pageReloadChecker,\n}", + "description": [], + "signature": [ + "Props" + ], + "path": "packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -575,7 +714,7 @@ "description": [ "\nAvatar stored in user profile." ], - "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "path": "packages/kbn-user-profile-components/src/types.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -591,7 +730,7 @@ "signature": [ "string | undefined" ], - "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "path": "packages/kbn-user-profile-components/src/types.ts", "deprecated": false, "trackAdoption": false }, @@ -607,7 +746,7 @@ "signature": [ "string | undefined" ], - "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "path": "packages/kbn-user-profile-components/src/types.ts", "deprecated": false, "trackAdoption": false }, @@ -623,13 +762,219 @@ "signature": [ "string | null | undefined" ], - "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "path": "packages/kbn-user-profile-components/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileData", + "type": "Interface", + "tags": [], + "label": "UserProfileData", + "description": [], + "path": "packages/kbn-user-profile-components/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileData.avatar", + "type": "Object", + "tags": [], + "label": "avatar", + "description": [], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileAvatarData", + "text": "UserProfileAvatarData" + }, + " | undefined" + ], + "path": "packages/kbn-user-profile-components/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileData.userSettings", + "type": "Object", + "tags": [], + "label": "userSettings", + "description": [], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserSettingsData", + "text": "UserSettingsData" + }, + " | undefined" + ], + "path": "packages/kbn-user-profile-components/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileData.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[key: string]: unknown", + "description": [], + "signature": [ + "[key: string]: unknown" + ], + "path": "packages/kbn-user-profile-components/src/types.ts", "deprecated": false, "trackAdoption": false } ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaDependencies", + "type": "Interface", + "tags": [], + "label": "UserProfilesKibanaDependencies", + "description": [ + "\nKibana-specific service types." + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaDependencies.core", + "type": "Object", + "tags": [], + "label": "core", + "description": [ + "CoreStart contract" + ], + "signature": [ + "{ notifications: ", + { + "pluginId": "@kbn/core-notifications-browser", + "scope": "common", + "docId": "kibKbnCoreNotificationsBrowserPluginApi", + "section": "def-common.NotificationsStart", + "text": "NotificationsStart" + }, + "; theme: ", + { + "pluginId": "@kbn/core-theme-browser", + "scope": "common", + "docId": "kibKbnCoreThemeBrowserPluginApi", + "section": "def-common.ThemeServiceStart", + "text": "ThemeServiceStart" + }, + "; }" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaDependencies.security", + "type": "Object", + "tags": [], + "label": "security", + "description": [], + "signature": [ + "{ userProfiles: ", + "UserProfileAPIClient", + "; }" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaDependencies.toMountPoint", + "type": "Function", + "tags": [], + "label": "toMountPoint", + "description": [ + "\nHandler from the '@kbn/kibana-react-plugin/public' Plugin\n\n```\nimport { toMountPoint } from '@kbn/kibana-react-plugin/public';\n```" + ], + "signature": [ + "(node: React.ReactNode, options?: { theme$: ", + "Observable", + "<{ readonly darkMode: boolean; }>; } | undefined) => ", + { + "pluginId": "@kbn/core-mount-utils-browser", + "scope": "common", + "docId": "kibKbnCoreMountUtilsBrowserPluginApi", + "section": "def-common.MountPoint", + "text": "MountPoint" + }, + "" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaDependencies.toMountPoint.$1", + "type": "CompoundType", + "tags": [], + "label": "node", + "description": [], + "signature": [ + "React.ReactNode" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaDependencies.toMountPoint.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesKibanaDependencies.toMountPoint.$2.theme$", + "type": "Object", + "tags": [], + "label": "theme$", + "description": [], + "signature": [ + "Observable", + "<{ readonly darkMode: boolean; }>" + ], + "path": "packages/kbn-user-profile-components/src/services.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/user-profile-components", "id": "def-common.UserProfilesPopoverProps", @@ -1093,6 +1438,43 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserSettingsData", + "type": "Interface", + "tags": [], + "label": "UserSettingsData", + "description": [ + "\nUser settings stored in the data object of the User Profile" + ], + "path": "packages/kbn-user-profile-components/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserSettingsData.darkMode", + "type": "CompoundType", + "tags": [], + "label": "darkMode", + "description": [], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.DarkModeValue", + "text": "DarkModeValue" + }, + " | undefined" + ], + "path": "packages/kbn-user-profile-components/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/user-profile-components", "id": "def-common.UserToolTipProps", @@ -1169,6 +1551,69 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.DarkModeValue", + "type": "Type", + "tags": [], + "label": "DarkModeValue", + "description": [], + "signature": [ + "\"\" | \"light\" | \"dark\"" + ], + "path": "packages/kbn-user-profile-components/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UpdateUserProfileHook", + "type": "Type", + "tags": [], + "label": "UpdateUserProfileHook", + "description": [], + "signature": [ + "({ notificationSuccess, pageReloadChecker, }?: Props) => { update: (updatedData: D) => Promise; showSuccessNotification: ({ isRefreshRequired }?: { isRefreshRequired?: boolean | undefined; }) => void; userProfileData: ", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileData", + "text": "UserProfileData" + }, + " | null | undefined; isLoading: boolean; }" + ], + "path": "packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UpdateUserProfileHook.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "Props" + ], + "path": "packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/user-profile-components", "id": "def-common.UserProfileWithAvatar", diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 25e3e061b5b02..691b204e0d289 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 58 | 0 | 5 | 0 | +| 80 | 0 | 21 | 2 | ## Common diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index a5f33d9761e5e..ae1e131251210 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-08-08 +date: 2023-08-10 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 247cf97516111..79efc58af5a46 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-08-08 +date: 2023-08-10 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 4483896e948fa..05d8ec753475f 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index 2399b8a856ad1..4979e45203494 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index b9927fd6e19f7..dac217dabfb5c 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-08-08 +date: 2023-08-10 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 6e1f9222238c7..bec775fbd5cde 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json index 7464f3e995e0b..1e490cd0d8e78 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -916,6 +916,18 @@ "plugin": "visualizations", "path": "src/plugins/visualizations/public/visualize_app/index.tsx" }, + { + "plugin": "serverless", + "path": "x-pack/plugins/serverless/public/plugin.tsx" + }, + { + "plugin": "serverless", + "path": "x-pack/plugins/serverless/public/plugin.tsx" + }, + { + "plugin": "serverless", + "path": "x-pack/plugins/serverless/public/plugin.tsx" + }, { "plugin": "controls", "path": "src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx" @@ -1136,18 +1148,6 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx" }, - { - "plugin": "alerting", - "path": "x-pack/plugins/alerting/public/application/maintenance_windows.tsx" - }, - { - "plugin": "alerting", - "path": "x-pack/plugins/alerting/public/application/maintenance_windows.tsx" - }, - { - "plugin": "alerting", - "path": "x-pack/plugins/alerting/public/application/maintenance_windows.tsx" - }, { "plugin": "security", "path": "x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx" @@ -1268,6 +1268,18 @@ "plugin": "security", "path": "x-pack/plugins/security/public/nav_control/nav_control_service.tsx" }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/public/application/maintenance_windows.tsx" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/public/application/maintenance_windows.tsx" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/public/application/maintenance_windows.tsx" + }, { "plugin": "triggersActionsUi", "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx" @@ -1292,18 +1304,6 @@ "plugin": "triggersActionsUi", "path": "x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx" }, - { - "plugin": "serverless", - "path": "x-pack/plugins/serverless/public/plugin.tsx" - }, - { - "plugin": "serverless", - "path": "x-pack/plugins/serverless/public/plugin.tsx" - }, - { - "plugin": "serverless", - "path": "x-pack/plugins/serverless/public/plugin.tsx" - }, { "plugin": "cases", "path": "x-pack/plugins/cases/public/application.tsx" @@ -1316,6 +1316,18 @@ "plugin": "cases", "path": "x-pack/plugins/cases/public/application.tsx" }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx" + }, { "plugin": "discover", "path": "src/plugins/discover/public/embeddable/saved_search_embeddable.tsx" @@ -1372,6 +1384,18 @@ "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/application/index.tsx" }, + { + "plugin": "observabilityAIAssistant", + "path": "x-pack/plugins/observability_ai_assistant/public/application.tsx" + }, + { + "plugin": "observabilityAIAssistant", + "path": "x-pack/plugins/observability_ai_assistant/public/application.tsx" + }, + { + "plugin": "observabilityAIAssistant", + "path": "x-pack/plugins/observability_ai_assistant/public/application.tsx" + }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/public/applications/integrations/app.tsx" @@ -3634,61 +3658,13 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts" }, - { - "plugin": "@kbn/ml-date-picker", - "path": "x-pack/packages/ml/date_picker/src/hooks/use_date_picker_context.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx" - }, - { - "plugin": "aiops", - "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx" - }, { "plugin": "security", - "path": "x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx" + "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx" + "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" }, { "plugin": "security", @@ -3758,6 +3734,70 @@ "plugin": "cases", "path": "x-pack/plugins/cases/public/components/visualizations/actions/add_to_existing_case.tsx" }, + { + "plugin": "@kbn/ml-date-picker", + "path": "x-pack/packages/ml/date_picker/src/hooks/use_date_picker_context.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx" + }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx" + }, { "plugin": "observabilityShared", "path": "x-pack/plugins/observability_shared/public/components/header_menu/header_menu_portal.tsx" @@ -3992,35 +4032,35 @@ }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/job_creation/common/create_flyout.tsx" + "path": "x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx" }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/job_creation/common/create_flyout.tsx" + "path": "x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx" }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx" + "path": "x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx" }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx" + "path": "x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx" }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx" + "path": "x-pack/plugins/ml/public/embeddables/job_creation/common/create_flyout.tsx" }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx" + "path": "x-pack/plugins/ml/public/embeddables/job_creation/common/create_flyout.tsx" }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx" + "path": "x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx" }, { "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx" + "path": "x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx" }, { "plugin": "ml", @@ -4138,6 +4178,14 @@ "plugin": "timelines", "path": "x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx" }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/take_action.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/take_action.tsx" + }, { "plugin": "runtimeFields", "path": "x-pack/plugins/runtime_fields/public/shared_imports.ts" @@ -4514,6 +4562,14 @@ "plugin": "uptime", "path": "x-pack/plugins/uptime/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx" }, + { + "plugin": "cloudLinks", + "path": "x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx" + }, + { + "plugin": "cloudLinks", + "path": "x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx" + }, { "plugin": "console", "path": "src/plugins/console/public/shared_imports.ts" diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index fd0b38bf12cb1..c7be1b62d86ec 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-08-08 +date: 2023-08-10 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 d0010371e0fea..4dcb18805a70f 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-08-08 +date: 2023-08-10 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 6f976bbdca7fa..d70815191743c 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index e33e34a0b36be..1a13adf50b509 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 0583a521ae2f9..ea90872093f0a 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-08-08 +date: 2023-08-10 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 128e3d18314c0..95d2563383ae5 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.devdocs.json b/api_docs/licensing.devdocs.json index e78f38fff12e2..9e94dad897ead 100644 --- a/api_docs/licensing.devdocs.json +++ b/api_docs/licensing.devdocs.json @@ -810,6 +810,10 @@ "plugin": "security", "path": "x-pack/plugins/security/public/plugin.tsx" }, + { + "plugin": "aiops", + "path": "x-pack/plugins/aiops/public/plugin.tsx" + }, { "plugin": "licenseManagement", "path": "x-pack/plugins/license_management/public/plugin.ts" diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 98d216c70b6d6..9bdde79f8dd6f 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-08-08 +date: 2023-08-10 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 0959f42b36730..e6be664391f64 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 4444e39542caf..2493ce0d839da 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 77219c568a9b9..28c2a79ba3ef0 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-08-08 +date: 2023-08-10 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 2191f1726b317..a760931ca5d51 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-08-08 +date: 2023-08-10 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 4a596b8f87948..4f61e1abe3a12 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-08-08 +date: 2023-08-10 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 aac94910e94b1..84fd36f31e5f5 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-08-08 +date: 2023-08-10 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 1ac07084a95b9..d61214544d53b 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-08-08 +date: 2023-08-10 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 b5e8501f6ebd4..f2ec25e486513 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-08-08 +date: 2023-08-10 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 f0e8b14832169..5edf0c3638b1d 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-08-08 +date: 2023-08-10 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 3b494d9a2daad..4ab7b81595545 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 8ebb28a12df00..ded2a49def137 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 60932d66f5c74..8cc7e821fa1a3 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -4371,7 +4371,7 @@ "label": "createOrUpdateIndex", "description": [], "signature": [ - "({\n index,\n mappings,\n settings,\n client,\n logger,\n}: { index: string; mappings: ", + "({\n index,\n mappings,\n client,\n logger,\n}: { index: string; mappings: ", "MappingTypeMapping", " & { date_detection?: boolean | undefined; dynamic?: ", "MappingDynamicMapping", @@ -4391,9 +4391,7 @@ "MappingSourceField", " | undefined; runtime?: ", "MappingRuntimeFields", - " | undefined; }; settings?: ", - "IndicesIndexSettings", - " | undefined; client: ", + " | undefined; }; client: ", { "pluginId": "@kbn/core-elasticsearch-server", "scope": "common", @@ -4420,7 +4418,7 @@ "id": "def-server.createOrUpdateIndex.$1", "type": "Object", "tags": [], - "label": "{\n index,\n mappings,\n settings,\n client,\n logger,\n}", + "label": "{\n index,\n mappings,\n client,\n logger,\n}", "description": [], "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", "deprecated": false, @@ -4470,21 +4468,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "observability", - "id": "def-server.createOrUpdateIndex.$1.settings", - "type": "CompoundType", - "tags": [], - "label": "settings", - "description": [], - "signature": [ - "IndicesIndexSettings", - " | undefined" - ], - "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "observability", "id": "def-server.createOrUpdateIndex.$1.client", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 2187ddd6940b1..0e2c22afa279d 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/actionable-observability](https://github.com/orgs/elastic/team | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 509 | 45 | 502 | 16 | +| 508 | 45 | 501 | 16 | ## Client diff --git a/api_docs/observability_a_i_assistant.devdocs.json b/api_docs/observability_a_i_assistant.devdocs.json index 6a07ae0c41f05..f2fd46f618d93 100644 --- a/api_docs/observability_a_i_assistant.devdocs.json +++ b/api_docs/observability_a_i_assistant.devdocs.json @@ -206,6 +206,17 @@ "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.Conversation.public", + "type": "boolean", + "tags": [], + "label": "public", + "description": [], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -240,7 +251,7 @@ "label": "message", "description": [], "signature": [ - "{ content?: string | undefined; name?: string | undefined; role: ", + "{ content?: string | undefined; name?: string | undefined; event?: string | undefined; role: ", { "pluginId": "observabilityAIAssistant", "scope": "common", @@ -248,15 +259,7 @@ "section": "def-common.MessageRole", "text": "MessageRole" }, - "; function_call?: { name: string; args?: ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.Serializable", - "text": "Serializable" - }, - "; trigger: ", + "; function_call?: { name: string; arguments?: string | undefined; trigger: ", { "pluginId": "observabilityAIAssistant", "scope": "common", @@ -280,15 +283,7 @@ "section": "def-common.MessageRole", "text": "MessageRole" }, - ".Elastic; } | undefined; data?: ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.Serializable", - "text": "Serializable" - }, - "; }" + ".Elastic; } | undefined; data?: string | undefined; }" ], "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", "deprecated": false, @@ -321,7 +316,115 @@ "label": "APIReturnType", "description": [], "signature": [ - "{ \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", + "{ \"POST /internal/observability_ai_assistant/functions/setup_kb\": { endpoint: \"POST /internal/observability_ai_assistant/functions/setup_kb\"; params?: undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + ") => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/summarise\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarise\"; params?: ", + "TypeC", + "<{ body: ", + "TypeC", + "<{ id: ", + "StringC", + "; text: ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">; confidence: ", + "UnionC", + "<[", + "LiteralC", + "<\"low\">, ", + "LiteralC", + "<\"medium\">, ", + "LiteralC", + "<\"high\">]>; is_correction: ", + "Type", + "; public: ", + "Type", + "; }>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { id: string; text: ", + "Branded", + "; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; }; }; }) => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/recall\": { endpoint: \"POST /internal/observability_ai_assistant/functions/recall\"; params?: ", + "TypeC", + "<{ body: ", + "TypeC", + "<{ query: ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">; }>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { query: ", + "Branded", + "; }; }; }) => Promise<{ entries: ", + "KnowledgeBaseEntry", + "[]; }>; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ method: ", + "UnionC", + "<[", + "LiteralC", + "<\"GET\">, ", + "LiteralC", + "<\"POST\">, ", + "LiteralC", + "<\"PATCH\">, ", + "LiteralC", + "<\"PUT\">, ", + "LiteralC", + "<\"DELETE\">]>; path: ", + "StringC", + "; }>, ", + "PartialC", + "<{ body: ", + "AnyC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<", { @@ -421,7 +524,15 @@ "StringC", "; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; }; }) => Promise; } & ", + " & { params: { path: { conversationId: string; }; }; }) => Promise<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Conversation", + "text": "Conversation" + }, + ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "TypeC", @@ -449,7 +560,21 @@ }, ", unknown>>; connectorId: ", "StringC", - "; }>; }> | undefined; handler: ({}: ", + "; functions: ", + "ArrayC", + "<", + "TypeC", + "<{ name: ", + "StringC", + "; description: ", + "StringC", + "; parameters: ", + "AnyC", + "; contexts: ", + "ArrayC", + "<", + "StringC", + ">; }>>; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", " & { params: { body: { messages: ", { @@ -459,7 +584,7 @@ "section": "def-common.Message", "text": "Message" }, - "[]; connectorId: string; }; }; }) => Promise<", + "[]; connectorId: string; functions: { name: string; description: string; parameters: any; contexts: string[]; }[]; }; }; }) => Promise<", "IncomingMessage", ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", @@ -480,7 +605,115 @@ "label": "ObservabilityAIAssistantAPIClientRequestParamsOf", "description": [], "signature": [ - "{ \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", + "{ \"POST /internal/observability_ai_assistant/functions/setup_kb\": { endpoint: \"POST /internal/observability_ai_assistant/functions/setup_kb\"; params?: undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + ") => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/summarise\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarise\"; params?: ", + "TypeC", + "<{ body: ", + "TypeC", + "<{ id: ", + "StringC", + "; text: ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">; confidence: ", + "UnionC", + "<[", + "LiteralC", + "<\"low\">, ", + "LiteralC", + "<\"medium\">, ", + "LiteralC", + "<\"high\">]>; is_correction: ", + "Type", + "; public: ", + "Type", + "; }>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { id: string; text: ", + "Branded", + "; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; }; }; }) => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/recall\": { endpoint: \"POST /internal/observability_ai_assistant/functions/recall\"; params?: ", + "TypeC", + "<{ body: ", + "TypeC", + "<{ query: ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">; }>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { query: ", + "Branded", + "; }; }; }) => Promise<{ entries: ", + "KnowledgeBaseEntry", + "[]; }>; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ method: ", + "UnionC", + "<[", + "LiteralC", + "<\"GET\">, ", + "LiteralC", + "<\"POST\">, ", + "LiteralC", + "<\"PATCH\">, ", + "LiteralC", + "<\"PUT\">, ", + "LiteralC", + "<\"DELETE\">]>; path: ", + "StringC", + "; }>, ", + "PartialC", + "<{ body: ", + "AnyC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<", { @@ -580,7 +813,15 @@ "StringC", "; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; }; }) => Promise; } & ", + " & { params: { path: { conversationId: string; }; }; }) => Promise<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Conversation", + "text": "Conversation" + }, + ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "TypeC", @@ -608,7 +849,21 @@ }, ", unknown>>; connectorId: ", "StringC", - "; }>; }> | undefined; handler: ({}: ", + "; functions: ", + "ArrayC", + "<", + "TypeC", + "<{ name: ", + "StringC", + "; description: ", + "StringC", + "; parameters: ", + "AnyC", + "; contexts: ", + "ArrayC", + "<", + "StringC", + ">; }>>; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", " & { params: { body: { messages: ", { @@ -618,7 +873,7 @@ "section": "def-common.Message", "text": "Message" }, - "[]; connectorId: string; }; }; }) => Promise<", + "[]; connectorId: string; functions: { name: string; description: string; parameters: any; contexts: string[]; }[]; }; }; }) => Promise<", "IncomingMessage", ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", @@ -647,7 +902,7 @@ "label": "ObservabilityAIAssistantAPIEndpoint", "description": [], "signature": [ - "\"POST /internal/observability_ai_assistant/chat\" | \"GET /internal/observability_ai_assistant/conversation/{conversationId}\" | \"POST /internal/observability_ai_assistant/conversations\" | \"PUT /internal/observability_ai_assistant/conversation\" | \"POST /internal/observability_ai_assistant/conversation/{conversationId}\" | \"DELETE /internal/observability_ai_assistant/conversation/{conversationId}\" | \"GET /internal/observability_ai_assistant/connectors\"" + "\"POST /internal/observability_ai_assistant/chat\" | \"GET /internal/observability_ai_assistant/conversation/{conversationId}\" | \"POST /internal/observability_ai_assistant/conversations\" | \"PUT /internal/observability_ai_assistant/conversation\" | \"POST /internal/observability_ai_assistant/conversation/{conversationId}\" | \"DELETE /internal/observability_ai_assistant/conversation/{conversationId}\" | \"GET /internal/observability_ai_assistant/connectors\" | \"POST /internal/observability_ai_assistant/functions/elasticsearch\" | \"POST /internal/observability_ai_assistant/functions/recall\" | \"POST /internal/observability_ai_assistant/functions/summarise\" | \"POST /internal/observability_ai_assistant/functions/setup_kb\"" ], "path": "x-pack/plugins/observability_ai_assistant/public/api/index.ts", "deprecated": false, @@ -691,7 +946,16321 @@ "path": "x-pack/plugins/observability_ai_assistant/public/types.ts", "deprecated": false, "trackAdoption": false, - "children": [], + "children": [ + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerContext", + "type": "Function", + "tags": [], + "label": "registerContext", + "description": [], + "signature": [ + "(options: ", + "ContextDefinition", + ") => void" + ], + "path": "x-pack/plugins/observability_ai_assistant/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerContext.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "ContextDefinition" + ], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerFunction", + "type": "Function", + "tags": [], + "label": "registerFunction", + "description": [], + "signature": [ + "(options: FunctionOptions, respond: RespondFunction, render?: RenderFunction | undefined) => void" + ], + "path": "x-pack/plugins/observability_ai_assistant/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerFunction.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "FunctionOptions" + ], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerFunction.$2", + "type": "Function", + "tags": [], + "label": "respond", + "description": [], + "signature": [ + "(options: { arguments: ", + "node_modules/ts-algebra/lib/meta-types/resolve", + "$Resolve<", + "JSONSchema7", + " extends (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) ? ", + "Any", + " : (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) extends string | true ? ", + "Any", + " : (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) extends false ? ", + "Never", + " : (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends string | true ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends false ? ", + "Never", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"$ref\"], \"#\">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "JSONSchema7", + " extends Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> ? ", + "Any", + " : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends string | true ? ", + "Any", + " : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends false ? ", + "Never", + " : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "ReferenceSchema", + " ? any : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends string | true ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends false ? ", + "Never", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"$ref\"], \"#\">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "JSONSchema7", + " extends Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> ? ", + "Any", + " : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends string | true ? ", + "Any", + " : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends false ? ", + "Never", + " : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "ReferenceSchema", + " ? any : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"enum\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { ...; }> : Omit<...> extends ", + "AnyOfSchema", + " ? ", + "If", + "<...> : Omit<...> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<...> : Omit<...> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + "<...> | ... 1 more ... | ", + "ParseConstSchema", + "<...> : Omit<...> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<...> : Omit<...> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + "<...> | ... 1 more ... | ", + "ParseSingleTypeSchema", + "<...> : ", + "Any", + "<...>> | ", + "ParseNullableSchema", + "<...> : Omit<...> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<...> : Omit<...> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<...> : Omit<...> extends ", + "OneOfSchema", + " ? ", + "If", + "<...> : Omit<...> extends ", + "AnyOfSchema", + " ? ", + "If", + "<...> : Omit<...> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<...> : Omit<...> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + "<...> | ... 1 more ... | ", + "ParseConstSchema", + "<...> : Omit<...> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<...> : Omit<...> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + "<...> | ... 1 more ... | ", + "ParseSingleTypeSchema", + "<...> : ", + "Any", + "<...>> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<...> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<...> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "OneOfSchema", + " ? ", + "If", + "<...> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "AnyOfSchema", + " ? ", + "If", + "<...> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<...> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + "<...> | ... 1 more ... | ", + "ParseConstSchema", + "<...> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<...> : (TParameters extends Record<...> ? ", + "DeepWritable", + "<...> : TParameters) extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + "<...> | ... 1 more ... | ", + "ParseSingleTypeSchema", + "<...> : ", + "Any", + "<...>, ", + "ResolveDefaultOptions", + ">; }, signal: AbortSignal) => Promise<...>" + ], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerFunction.$2.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "{ arguments: ", + "node_modules/ts-algebra/lib/meta-types/resolve", + "$Resolve<", + "JSONSchema7", + " extends (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) ? ", + "Any", + " : (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) extends string | true ? ", + "Any", + " : (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) extends false ? ", + "Never", + " : (TParameters extends Record ? ", + "DeepWritable", + " : TParameters) extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends string | true ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends false ? ", + "Never", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"$ref\"], \"#\">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "MultipleTypesSchema", + " ? any : any> : any> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends string | true ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends false ? ", + "Never", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"$ref\"], \"#\">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "MultipleTypesSchema", + " ? any : any> : any> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends string | true ? ", + "Any", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends false ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "AnyOfSchema", + " ? ", + "Never", + " : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "MultipleTypesSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnMixedSchema<(", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">)[\"type\"], ", + "MultipleTypesSchema", + " & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\"> extends ", + "SingleTypeSchema", + " ? ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseSingleTypeSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { type: ", + "JSONSchema7TypeName", + "; } & Omit<", + "NullableSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : ", + "Any", + "> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"$ref\"], \"#\">> : Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "JSONSchema7", + " extends Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends string | true ? ", + "Any", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends false ? ", + "Never", + " : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "NullableSchema", + " ? ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> | ", + "ParseNullableSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { nullable: boolean; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ReferenceSchema", + " ? ", + "ParseReferenceSchema", + "<", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, RecSplit<(", + "ReferenceSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"$ref\"], \"#\">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AllOfSchema", + " ? RecurseOnAllOfSchema<(", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"allOf\"], ", + "AllOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "OneOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnOneOfSchema<(", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"oneOf\"], ", + "OneOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "AnyOfSchema", + " ? ", + "If", + "<", + "IsNever", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>>, ", + "Never", + ", ", + "DoesExtend", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>, ", + "Never", + "> extends true ? ", + "Never", + " : { type: \"union\"; values: RecurseOnAnyOfSchema<(", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"anyOf\"], ", + "AnyOfSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">, never>; }> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "EnumSchema", + " ? ", + "node_modules/ts-algebra/lib/meta-types/intersection/index", + "$Intersect<", + "If", + "<", + "IsNever", + "<", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>>, ", + "Never", + ", { type: \"enum\"; values: ", + "Compute", + "<(", + "EnumSchema", + " & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">)[\"enum\"][number], never>; isSerialized: false; deserialized: never; }>, ", + "Any", + "> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "ConstSchema", + " ? ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> | ", + "ParseConstSchema", + " & { const?: unknown; enum?: unknown; items?: ", + "JSONSchema7", + " | ", + "JSONSchema7", + "[] | undefined; additionalItems?: ", + "JSONSchema7", + " | undefined; contains?: ", + "JSONSchema7", + " | undefined; properties?: Record | undefined; patternProperties?: Record | undefined; additionalProperties?: ", + "JSONSchema7", + " | undefined; dependencies?: { [key: string]: string[] | ", + "JSONSchema7", + "; } | undefined; propertyNames?: ", + "JSONSchema7", + " | undefined; if?: ", + "JSONSchema7", + " | undefined; then?: ", + "JSONSchema7", + " | undefined; else?: ", + "JSONSchema7", + " | undefined; allOf?: ", + "JSONSchema7", + "[] | undefined; anyOf?: ", + "JSONSchema7", + "[] | undefined; oneOf?: ", + "JSONSchema7", + "[] | undefined; not?: ", + "JSONSchema7", + " | undefined; nullable?: boolean | undefined; definitions?: { [key: string]: ", + "JSONSchema7", + "; } | undefined; examples?: unknown[] | undefined; default?: unknown; [$JSONSchema7]?: typeof ", + "node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema7", + "$JSONSchema7 | undefined; } & { const: unknown; } & Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\">, ", + "ParseOptions", + " ? ", + "DeepWritable", + " : TParameters, ", + "FromSchemaDefaultOptions", + ">> : Omit<", + "AllOfSchema", + " & Omit<", + "NullableSchema", + " & (TParameters extends Record ? ", + "DeepWritable", + " : TParameters), \"nullable\">, \"allOf\"> extends ", + "MultipleTypesSchema", + " ? any : any> : any> : any, ", + "ResolveDefaultOptions", + ">; }" + ], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerFunction.$2.$2", + "type": "Object", + "tags": [], + "label": "signal", + "description": [], + "signature": [ + "AbortSignal" + ], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-public.ObservabilityAIAssistantPluginStart.registerFunction.$3", + "type": "Function", + "tags": [], + "label": "render", + "description": [], + "signature": [ + "RenderFunction | undefined" + ], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], "lifecycle": "start", "initialIsOpen": true } @@ -710,7 +17279,115 @@ "label": "ObservabilityAIAssistantServerRouteRepository", "description": [], "signature": [ - "{ \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", + "{ \"POST /internal/observability_ai_assistant/functions/setup_kb\": { endpoint: \"POST /internal/observability_ai_assistant/functions/setup_kb\"; params?: undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + ") => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/summarise\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarise\"; params?: ", + "TypeC", + "<{ body: ", + "TypeC", + "<{ id: ", + "StringC", + "; text: ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">; confidence: ", + "UnionC", + "<[", + "LiteralC", + "<\"low\">, ", + "LiteralC", + "<\"medium\">, ", + "LiteralC", + "<\"high\">]>; is_correction: ", + "Type", + "; public: ", + "Type", + "; }>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { id: string; text: ", + "Branded", + "; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; }; }; }) => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/recall\": { endpoint: \"POST /internal/observability_ai_assistant/functions/recall\"; params?: ", + "TypeC", + "<{ body: ", + "TypeC", + "<{ query: ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">; }>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { query: ", + "Branded", + "; }; }; }) => Promise<{ entries: ", + "KnowledgeBaseEntry", + "[]; }>; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ method: ", + "UnionC", + "<[", + "LiteralC", + "<\"GET\">, ", + "LiteralC", + "<\"POST\">, ", + "LiteralC", + "<\"PATCH\">, ", + "LiteralC", + "<\"PUT\">, ", + "LiteralC", + "<\"DELETE\">]>; path: ", + "StringC", + "; }>, ", + "PartialC", + "<{ body: ", + "AnyC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<", { @@ -810,7 +17487,15 @@ "StringC", "; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; }; }) => Promise; } & ", + " & { params: { path: { conversationId: string; }; }; }) => Promise<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Conversation", + "text": "Conversation" + }, + ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "TypeC", @@ -838,7 +17523,21 @@ }, ", unknown>>; connectorId: ", "StringC", - "; }>; }> | undefined; handler: ({}: ", + "; functions: ", + "ArrayC", + "<", + "TypeC", + "<{ name: ", + "StringC", + "; description: ", + "StringC", + "; parameters: ", + "AnyC", + "; contexts: ", + "ArrayC", + "<", + "StringC", + ">; }>>; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", " & { params: { body: { messages: ", { @@ -848,7 +17547,7 @@ "section": "def-common.Message", "text": "Message" }, - "[]; connectorId: string; }; }; }) => Promise<", + "[]; connectorId: string; functions: { name: string; description: string; parameters: any; contexts: string[]; }[]; }; }; }) => Promise<", "IncomingMessage", ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", @@ -975,6 +17674,17 @@ "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-common.Conversation.public", + "type": "boolean", + "tags": [], + "label": "public", + "description": [], + "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -1009,7 +17719,7 @@ "label": "message", "description": [], "signature": [ - "{ content?: string | undefined; name?: string | undefined; role: ", + "{ content?: string | undefined; name?: string | undefined; event?: string | undefined; role: ", { "pluginId": "observabilityAIAssistant", "scope": "common", @@ -1017,15 +17727,7 @@ "section": "def-common.MessageRole", "text": "MessageRole" }, - "; function_call?: { name: string; args?: ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.Serializable", - "text": "Serializable" - }, - "; trigger: ", + "; function_call?: { name: string; arguments?: string | undefined; trigger: ", { "pluginId": "observabilityAIAssistant", "scope": "common", @@ -1049,15 +17751,7 @@ "section": "def-common.MessageRole", "text": "MessageRole" }, - ".Elastic; } | undefined; data?: ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.Serializable", - "text": "Serializable" - }, - "; }" + ".Elastic; } | undefined; data?: string | undefined; }" ], "path": "x-pack/plugins/observability_ai_assistant/common/types.ts", "deprecated": false, diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index f2868024f51cb..fb04665ad7c31 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 35 | 0 | 33 | 5 | +| 45 | 0 | 43 | 7 | ## Client diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 8499bbadd51b3..a876771d94e36 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.devdocs.json b/api_docs/observability_shared.devdocs.json index 3e93ed26a1aed..4f105830edc04 100644 --- a/api_docs/observability_shared.devdocs.json +++ b/api_docs/observability_shared.devdocs.json @@ -595,7 +595,7 @@ "label": "noCasesPermissions", "description": [], "signature": [ - "() => { all: boolean; create: boolean; read: boolean; update: boolean; delete: boolean; push: boolean; }" + "() => { all: boolean; create: boolean; read: boolean; update: boolean; delete: boolean; push: boolean; connectors: boolean; }" ], "path": "x-pack/plugins/observability_shared/public/utils/cases_permissions.ts", "deprecated": false, diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index b00388af882bc..49a91b491b06f 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 11d520ff3b5e0..a784ef7b6abfe 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-08-08 +date: 2023-08-10 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 5836f5e27a50e..399e71eadb82b 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 71820 | 556 | 61367 | 1472 | +| 71867 | 556 | 61386 | 1474 | ## Plugin Directory @@ -29,14 +29,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] |--------------|----------------|-----------|--------------|----------|---------------|--------| | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 275 | 10 | 269 | 27 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 1 | 32 | 2 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 60 | 1 | 0 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 60 | 1 | 3 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 780 | 1 | 749 | 46 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 48 | 0 | 48 | 113 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | Asset manager plugin for entity assets (inventory, topology, etc) | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 91 | 1 | 75 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 93 | 0 | 74 | 27 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 94 | 0 | 75 | 27 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 268 | 16 | 253 | 10 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 16 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Chat available on Elastic Cloud deployments for quicker assistance. | 3 | 0 | 2 | 0 | @@ -56,24 +56,24 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 268 | 0 | 249 | 1 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 100 | 0 | 98 | 9 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3295 | 119 | 2573 | 27 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3301 | 119 | 2575 | 27 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 16 | 0 | 7 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Reusable data view field editor across Kibana | 72 | 0 | 33 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data view management app | 2 | 0 | 2 | 0 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 1012 | 0 | 243 | 2 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 1024 | 0 | 246 | 2 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 31 | 3 | 25 | 1 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 12 | 0 | 10 | 3 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 78 | 0 | 51 | 15 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | | discoverLogExplorer | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | This plugin exposes and registers Logs+ features. | 0 | 0 | 0 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 535 | 11 | 437 | 7 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 536 | 11 | 438 | 7 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Extends embeddable plugin with more functionality | 14 | 0 | 14 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 51 | 0 | 44 | 0 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | Adds dashboards for discovering and managing Enterprise Search products. | 10 | 0 | 10 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 115 | 3 | 111 | 3 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | The Event Annotation service contains expressions for event annotations | 192 | 30 | 192 | 2 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 116 | 0 | 116 | 11 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | The Event Annotation service contains expressions for event annotations | 191 | 30 | 191 | 2 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 111 | 0 | 111 | 11 | | | [@elastic/uptime](https://github.com/orgs/elastic/teams/uptime) | - | 131 | 1 | 131 | 14 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'error' renderer to expressions | 17 | 0 | 15 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Gauge plugin adds a `gauge` renderer and function to the expression plugin. The renderer will display the `gauge` chart. | 59 | 0 | 59 | 2 | @@ -132,8 +132,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | -| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 509 | 45 | 502 | 16 | -| | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 35 | 0 | 33 | 5 | +| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 508 | 45 | 501 | 16 | +| | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 45 | 0 | 43 | 7 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 14 | 0 | 14 | 0 | | | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 277 | 1 | 276 | 11 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 24 | 0 | 24 | 7 | @@ -146,16 +146,16 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 264 | 0 | 235 | 14 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 24 | 0 | 19 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 129 | 2 | 118 | 4 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 44 | 0 | 44 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 154 | 0 | 140 | 2 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 25 | 0 | 25 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 161 | 0 | 147 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 79 | 0 | 73 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 100 | 0 | 52 | 1 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the definition and helper methods around saved searches, used by discover and visualizations. | 72 | 0 | 71 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 32 | 0 | 13 | 0 | | | [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 8 | 5 | | searchprofiler | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 277 | 0 | 89 | 4 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 192 | 2 | 126 | 32 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 270 | 0 | 87 | 3 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 194 | 2 | 128 | 32 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | ESS customizations for Security Solution. | 6 | 0 | 6 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Serverless customizations for security. | 6 | 0 | 6 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | The core Serverless plugin, providing APIs to Serverless Project plugins. | 17 | 0 | 16 | 0 | @@ -178,8 +178,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 257 | 1 | 213 | 22 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [@elastic/kibana-localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 564 | 12 | 538 | 50 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 144 | 2 | 102 | 9 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 570 | 12 | 544 | 50 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 145 | 2 | 103 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 206 | 0 | 140 | 9 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | The `unifiedHistogram` plugin provides UI components to create a layout including a resizable histogram and a main display. | 53 | 0 | 23 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 142 | 2 | 104 | 22 | @@ -210,8 +210,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Package name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 11 | 5 | 11 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 30 | 0 | 6 | 1 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 12 | 0 | 0 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 33 | 0 | 0 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 20 | 0 | 0 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 16 | 0 | 15 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 24 | 0 | 24 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 8 | 0 | 7 | 1 | @@ -245,7 +245,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 14 | 0 | 14 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 58 | 0 | 40 | 4 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 187 | 1 | 122 | 0 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 188 | 1 | 123 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 7 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | @@ -600,7 +600,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 7 | 0 | 6 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list and field stats which can be integrated into apps | 306 | 0 | 277 | 9 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 4 | 0 | 0 | 0 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 58 | 0 | 5 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 80 | 0 | 21 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 0 | 15 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 24 | 0 | 14 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 0fe8f9a7d353e..99f4c28514e59 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 0b39ac1b4e63c..334556cc3774f 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 0ff4e46e2a510..559e1229116e7 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-08-08 +date: 2023-08-10 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 112b4b29f3ae1..bc9d258b05d1d 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-08-08 +date: 2023-08-10 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 f8272bfef1d9f..93d9d8abaf5e3 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-08-08 +date: 2023-08-10 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 274209f68d7d7..8d5eced80baa4 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-08-08 +date: 2023-08-10 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 589623de8182e..42b5675dedaa9 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-08-08 +date: 2023-08-10 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 a71ec6f5e7609..272abd228e1af 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.devdocs.json b/api_docs/saved_objects_finder.devdocs.json index 9ae6255f9aefe..5de85a8979f27 100644 --- a/api_docs/saved_objects_finder.devdocs.json +++ b/api_docs/saved_objects_finder.devdocs.json @@ -11,7 +11,15 @@ "label": "getSavedObjectFinder", "description": [], "signature": [ - "(uiSettings: ", + "(contentClient: ", + { + "pluginId": "contentManagement", + "scope": "public", + "docId": "kibContentManagementPluginApi", + "section": "def-public.ContentClient", + "text": "ContentClient" + }, + ", uiSettings: ", { "pluginId": "@kbn/core-ui-settings-browser", "scope": "common", @@ -19,22 +27,6 @@ "section": "def-common.IUiSettingsClient", "text": "IUiSettingsClient" }, - ", http: ", - { - "pluginId": "@kbn/core-http-browser", - "scope": "common", - "docId": "kibKbnCoreHttpBrowserPluginApi", - "section": "def-common.HttpSetup", - "text": "HttpSetup" - }, - ", savedObjectsManagement: ", - { - "pluginId": "savedObjectsManagement", - "scope": "public", - "docId": "kibSavedObjectsManagementPluginApi", - "section": "def-public.SavedObjectsManagementPluginStart", - "text": "SavedObjectsManagementPluginStart" - }, ", savedObjectsTagging?: ", { "pluginId": "savedObjectsTaggingOss", @@ -62,15 +54,15 @@ "id": "def-public.getSavedObjectFinder.$1", "type": "Object", "tags": [], - "label": "uiSettings", + "label": "contentClient", "description": [], "signature": [ { - "pluginId": "@kbn/core-ui-settings-browser", - "scope": "common", - "docId": "kibKbnCoreUiSettingsBrowserPluginApi", - "section": "def-common.IUiSettingsClient", - "text": "IUiSettingsClient" + "pluginId": "contentManagement", + "scope": "public", + "docId": "kibContentManagementPluginApi", + "section": "def-public.ContentClient", + "text": "ContentClient" } ], "path": "src/plugins/saved_objects_finder/public/finder/index.tsx", @@ -83,15 +75,15 @@ "id": "def-public.getSavedObjectFinder.$2", "type": "Object", "tags": [], - "label": "http", + "label": "uiSettings", "description": [], "signature": [ { - "pluginId": "@kbn/core-http-browser", + "pluginId": "@kbn/core-ui-settings-browser", "scope": "common", - "docId": "kibKbnCoreHttpBrowserPluginApi", - "section": "def-common.HttpSetup", - "text": "HttpSetup" + "docId": "kibKbnCoreUiSettingsBrowserPluginApi", + "section": "def-common.IUiSettingsClient", + "text": "IUiSettingsClient" } ], "path": "src/plugins/saved_objects_finder/public/finder/index.tsx", @@ -104,27 +96,6 @@ "id": "def-public.getSavedObjectFinder.$3", "type": "Object", "tags": [], - "label": "savedObjectsManagement", - "description": [], - "signature": [ - { - "pluginId": "savedObjectsManagement", - "scope": "public", - "docId": "kibSavedObjectsManagementPluginApi", - "section": "def-public.SavedObjectsManagementPluginStart", - "text": "SavedObjectsManagementPluginStart" - } - ], - "path": "src/plugins/saved_objects_finder/public/finder/index.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-public.getSavedObjectFinder.$4", - "type": "Object", - "tags": [], "label": "savedObjectsTagging", "description": [], "signature": [ @@ -431,29 +402,31 @@ "parentPluginId": "savedObjectsFinder", "id": "def-public.SavedObjectMetaData.includeFields", "type": "Array", - "tags": [], + "tags": [ + "deprecated" + ], "label": "includeFields", "description": [], "signature": [ "string[] | undefined" ], "path": "src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-public.SavedObjectMetaData.defaultSearchField", - "type": "string", - "tags": [], - "label": "defaultSearchField", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx", - "deprecated": false, - "trackAdoption": false + "deprecated": true, + "trackAdoption": false, + "references": [ + { + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx" + }, + { + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx" + }, + { + "plugin": "graph", + "path": "x-pack/plugins/graph/public/components/source_picker.tsx" + } + ] } ], "initialIsOpen": false @@ -529,249 +502,6 @@ "path": "src/plugins/saved_objects_finder/common/types.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FinderAttributes.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP", - "type": "Interface", - "tags": [], - "label": "FindQueryHTTP", - "description": [], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.perPage", - "type": "number", - "tags": [], - "label": "perPage", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.page", - "type": "number", - "tags": [], - "label": "page", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.type", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string | string[]" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.search", - "type": "string", - "tags": [], - "label": "search", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.searchFields", - "type": "Array", - "tags": [], - "label": "searchFields", - "description": [], - "signature": [ - "string[] | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.defaultSearchOperator", - "type": "CompoundType", - "tags": [], - "label": "defaultSearchOperator", - "description": [], - "signature": [ - "\"AND\" | \"OR\" | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.sortField", - "type": "string", - "tags": [], - "label": "sortField", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.sortOrder", - "type": "CompoundType", - "tags": [], - "label": "sortOrder", - "description": [], - "signature": [ - "\"asc\" | \"desc\" | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.fields", - "type": "CompoundType", - "tags": [], - "label": "fields", - "description": [], - "signature": [ - "string | string[] | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindQueryHTTP.hasReference", - "type": "string", - "tags": [], - "label": "hasReference", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindResponseHTTP", - "type": "Interface", - "tags": [], - "label": "FindResponseHTTP", - "description": [], - "signature": [ - { - "pluginId": "savedObjectsFinder", - "scope": "common", - "docId": "kibSavedObjectsFinderPluginApi", - "section": "def-common.FindResponseHTTP", - "text": "FindResponseHTTP" - }, - "" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindResponseHTTP.saved_objects", - "type": "Array", - "tags": [], - "label": "saved_objects", - "description": [], - "signature": [ - { - "pluginId": "savedObjectsFinder", - "scope": "common", - "docId": "kibSavedObjectsFinderPluginApi", - "section": "def-common.SavedObjectCommon", - "text": "SavedObjectCommon" - }, - "[]" - ], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindResponseHTTP.total", - "type": "number", - "tags": [], - "label": "total", - "description": [], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindResponseHTTP.page", - "type": "number", - "tags": [], - "label": "page", - "description": [], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "savedObjectsFinder", - "id": "def-common.FindResponseHTTP.per_page", - "type": "number", - "tags": [], - "label": "per_page", - "description": [], - "path": "src/plugins/saved_objects_finder/common/types.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -818,11 +548,11 @@ "description": [], "signature": [ { - "pluginId": "@kbn/core-saved-objects-common", + "pluginId": "@kbn/content-management-utils", "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" + "docId": "kibKbnContentManagementUtilsPluginApi", + "section": "def-common.SOWithMetadata", + "text": "SOWithMetadata" }, "" ], diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index e37e0a65e9f47..8d56eb66947c8 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 44 | 0 | 44 | 0 | +| 25 | 0 | 25 | 0 | ## Client diff --git a/api_docs/saved_objects_management.devdocs.json b/api_docs/saved_objects_management.devdocs.json index 44279045cfc47..958d4058e2682 100644 --- a/api_docs/saved_objects_management.devdocs.json +++ b/api_docs/saved_objects_management.devdocs.json @@ -457,6 +457,153 @@ } ], "functions": [ + { + "parentPluginId": "savedObjectsManagement", + "id": "def-public.getTagFindReferences", + "type": "Function", + "tags": [], + "label": "getTagFindReferences", + "description": [], + "signature": [ + "({ selectedTags, taggingApi, }: { selectedTags?: string[] | undefined; taggingApi?: ", + { + "pluginId": "savedObjectsTaggingOss", + "scope": "public", + "docId": "kibSavedObjectsTaggingOssPluginApi", + "section": "def-public.SavedObjectsTaggingApi", + "text": "SavedObjectsTaggingApi" + }, + " | undefined; }) => ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + "[] | undefined" + ], + "path": "src/plugins/saved_objects_management/public/lib/get_tag_references.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "savedObjectsManagement", + "id": "def-public.getTagFindReferences.$1", + "type": "Object", + "tags": [], + "label": "{\n selectedTags,\n taggingApi,\n}", + "description": [], + "path": "src/plugins/saved_objects_management/public/lib/get_tag_references.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "savedObjectsManagement", + "id": "def-public.getTagFindReferences.$1.selectedTags", + "type": "Array", + "tags": [], + "label": "selectedTags", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "src/plugins/saved_objects_management/public/lib/get_tag_references.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "savedObjectsManagement", + "id": "def-public.getTagFindReferences.$1.taggingApi", + "type": "Object", + "tags": [], + "label": "taggingApi", + "description": [], + "signature": [ + { + "pluginId": "savedObjectsTaggingOss", + "scope": "public", + "docId": "kibSavedObjectsTaggingOssPluginApi", + "section": "def-public.SavedObjectsTaggingApi", + "text": "SavedObjectsTaggingApi" + }, + " | undefined" + ], + "path": "src/plugins/saved_objects_management/public/lib/get_tag_references.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "savedObjectsManagement", + "id": "def-public.parseQuery", + "type": "Function", + "tags": [], + "label": "parseQuery", + "description": [], + "signature": [ + "(query: ", + "Query", + ", types: ", + { + "pluginId": "savedObjectsManagement", + "scope": "common", + "docId": "kibSavedObjectsManagementPluginApi", + "section": "def-common.SavedObjectManagementTypeInfo", + "text": "SavedObjectManagementTypeInfo" + }, + "[]) => ParsedQuery" + ], + "path": "src/plugins/saved_objects_management/public/lib/parse_query.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "savedObjectsManagement", + "id": "def-public.parseQuery.$1", + "type": "Object", + "tags": [], + "label": "query", + "description": [], + "signature": [ + "Query" + ], + "path": "src/plugins/saved_objects_management/public/lib/parse_query.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "savedObjectsManagement", + "id": "def-public.parseQuery.$2", + "type": "Array", + "tags": [], + "label": "types", + "description": [], + "signature": [ + { + "pluginId": "savedObjectsManagement", + "scope": "common", + "docId": "kibSavedObjectsManagementPluginApi", + "section": "def-common.SavedObjectManagementTypeInfo", + "text": "SavedObjectManagementTypeInfo" + }, + "[]" + ], + "path": "src/plugins/saved_objects_management/public/lib/parse_query.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "savedObjectsManagement", "id": "def-public.processImportResponse", diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 2af10a0ba1a5c..d395a34dd7749 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 154 | 0 | 140 | 2 | +| 161 | 0 | 147 | 2 | ## Client diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 9453cbd5adb2c..1d089c75628e9 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-08-08 +date: 2023-08-10 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 0461d38388a45..73d9712e702cb 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-08-08 +date: 2023-08-10 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 e120f71b28e4b..fe238a6cb2d05 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-08-08 +date: 2023-08-10 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 a2d304cc86e66..3c672d14ca80b 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-08-08 +date: 2023-08-10 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 6f8e21a4b85fa..db5a6c76c83e6 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.devdocs.json b/api_docs/security.devdocs.json index 167308664bcd8..7246728a30a37 100644 --- a/api_docs/security.devdocs.json +++ b/api_docs/security.devdocs.json @@ -999,42 +999,6 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-public.UpdateUserProfileHook", - "type": "Type", - "tags": [], - "label": "UpdateUserProfileHook", - "description": [], - "signature": [ - "(props?: Props | undefined) => { update: (data: ", - "UserProfileData", - ") => void; showSuccessNotification: (props: { isRefreshRequired: boolean; }) => void; isLoading: boolean; userProfileData?: ", - "UserProfileData", - " | null | undefined; }" - ], - "path": "x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "security", - "id": "def-public.UpdateUserProfileHook.$1", - "type": "Object", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "Props | undefined" - ], - "path": "x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false } ], "objects": [], @@ -1163,9 +1127,21 @@ ], "signature": [ "{ update: (data: D) => Promise; suggest: (path: string, params: ", { "pluginId": "security", @@ -1183,7 +1159,13 @@ "text": "UserProfile" }, "[]>; bulkGet: (params: ", { "pluginId": "security", @@ -1201,7 +1183,13 @@ "text": "UserProfile" }, "[]>; getCurrent: (params?: ", { "pluginId": "security", @@ -1221,32 +1209,14 @@ ">; readonly userProfile$: ", "Observable", "<", - "UserProfileData", - " | null>; }" - ], - "path": "x-pack/plugins/security/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-public.SecurityPluginStart.hooks", - "type": "Object", - "tags": [], - "label": "hooks", - "description": [ - "\nA set of hooks to work with Kibana user profiles" - ], - "signature": [ - "{ useUpdateUserProfile: ", { - "pluginId": "security", - "scope": "public", - "docId": "kibSecurityPluginApi", - "section": "def-public.UpdateUserProfileHook", - "text": "UpdateUserProfileHook" + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileData", + "text": "UserProfileData" }, - "; }" + " | null>; }" ], "path": "x-pack/plugins/security/public/plugin.tsx", "deprecated": false, @@ -4849,70 +4819,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "security", - "id": "def-common.UserProfileAvatarData", - "type": "Interface", - "tags": [], - "label": "UserProfileAvatarData", - "description": [ - "\nAvatar stored in user profile." - ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "security", - "id": "def-common.UserProfileAvatarData.initials", - "type": "string", - "tags": [], - "label": "initials", - "description": [ - "\nOptional initials (two letters) of the user to use as avatar if avatar picture isn't specified." - ], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-common.UserProfileAvatarData.color", - "type": "string", - "tags": [], - "label": "color", - "description": [ - "\nBackground color of the avatar when initials are used." - ], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-common.UserProfileAvatarData.imageUrl", - "type": "CompoundType", - "tags": [], - "label": "imageUrl", - "description": [ - "\nBase64 data URL for the user avatar image." - ], - "signature": [ - "string | null | undefined" - ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "security", "id": "def-common.UserProfileUserInfo", diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 292adb907ed5c..c11d842bd6a54 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 277 | 0 | 89 | 4 | +| 270 | 0 | 87 | 3 | ## Client diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index c662e145879ef..62ef5e036ebbd 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -698,6 +698,46 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "securitySolution", + "id": "def-public.UpsellingService.getSectionsValue", + "type": "Function", + "tags": [], + "label": "getSectionsValue", + "description": [], + "signature": [ + "() => Map<", + { + "pluginId": "securitySolution", + "scope": "public", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-public.UpsellingSectionId", + "text": "UpsellingSectionId" + }, + ", React.ComponentType<{}>>" + ], + "path": "x-pack/plugins/security_solution/public/common/lib/upsellings/upselling_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "securitySolution", + "id": "def-public.UpsellingService.getMessagesValue", + "type": "Function", + "tags": [], + "label": "getMessagesValue", + "description": [], + "signature": [ + "() => Map<\"investigation_guide\", string>" + ], + "path": "x-pack/plugins/security_solution/public/common/lib/upsellings/upselling_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false @@ -2045,7 +2085,7 @@ "label": "SectionUpsellings", "description": [], "signature": [ - "{ entity_analytics_panel?: React.ComponentType<{}> | undefined; endpointPolicyProtections?: React.ComponentType<{}> | undefined; }" + "{ entity_analytics_panel?: React.ComponentType<{}> | undefined; endpointPolicyProtections?: React.ComponentType<{}> | undefined; osquery_automated_response_actions?: React.ComponentType<{}> | undefined; }" ], "path": "x-pack/plugins/security_solution/public/common/lib/upsellings/types.ts", "deprecated": false, @@ -2060,7 +2100,7 @@ "label": "UpsellingSectionId", "description": [], "signature": [ - "\"entity_analytics_panel\" | \"endpointPolicyProtections\"" + "\"entity_analytics_panel\" | \"endpointPolicyProtections\" | \"osquery_automated_response_actions\"" ], "path": "x-pack/plugins/security_solution/public/common/lib/upsellings/types.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index c64f8c3f50052..90c9f0ff7cde5 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 192 | 2 | 126 | 32 | +| 194 | 2 | 128 | 32 | ## Client diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index a9739d6975107..021a68466fba6 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index ba832361ce496..01b4591d7d5cb 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 459fdbaba17f1..d74013cbdb736 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index 5f07e440f3f62..3754e22fb88fa 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index d0d3c45b346be..d47db0b6fd0fd 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 36c1c386fe626..6253d454a38f7 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-08-08 +date: 2023-08-10 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 dd82b7a7820c6..a0434ac17a210 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-08-08 +date: 2023-08-10 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 f8ab5e8b4628b..0a17014d3618b 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-08-08 +date: 2023-08-10 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 53d46e39ab72c..2c72ab3b89813 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-08-08 +date: 2023-08-10 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 7e05dfd474d7a..c7554bc62fcf2 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 0bf6d4874a9a8..86bf629608c0a 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 868d29e007c8f..31642e5fbb806 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-08-08 +date: 2023-08-10 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 746ecac6f05e1..d8cc15b0e578c 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-08-08 +date: 2023-08-10 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 0c6166a26328a..d6f986fc4a44c 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-08-08 +date: 2023-08-10 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 ade3334eb49c2..40cb64ab84f17 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-08-08 +date: 2023-08-10 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 b0ee09e8d8339..450979f04eccb 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index 5caa041d59d6d..eed262ea515d7 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index e134dec835ed6..34122a05b3b95 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-08-08 +date: 2023-08-10 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 a8e4b3b5a20f8..63d40f7edeff2 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-08-08 +date: 2023-08-10 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 4d0f82ab99596..4b2f1c6453716 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index 2c72f5d31c911..4e3966581d22a 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -450,6 +450,112 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.executeAction", + "type": "Function", + "tags": [], + "label": "executeAction", + "description": [], + "signature": [ + "({\n id,\n params,\n http,\n signal,\n}: { id: string; http: ", + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + }, + "; params: Record; signal?: AbortSignal | undefined; }) => Promise<", + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ActionTypeExecutorResult", + "text": "ActionTypeExecutorResult" + }, + ">" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.executeAction.$1", + "type": "Object", + "tags": [], + "label": "{\n id,\n params,\n http,\n signal,\n}", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.executeAction.$1.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.executeAction.$1.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.executeAction.$1.params", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "{ [x: string]: unknown; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.executeAction.$1.signal", + "type": "Object", + "tags": [], + "label": "signal", + "description": [], + "signature": [ + "AbortSignal | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.ForLastExpression", @@ -1429,7 +1535,7 @@ "label": "TextAreaWithMessageVariables", "description": [], "signature": [ - "({ messageVariables, paramsProperty, index, inputTargetValue, isDisabled, editAction, label, errors, }: React.PropsWithChildren) => JSX.Element" + "({ messageVariables, paramsProperty, index, inputTargetValue, isDisabled, editAction, label, errors, helpText, }: React.PropsWithChildren) => JSX.Element" ], "path": "x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx", "deprecated": false, @@ -1440,7 +1546,7 @@ "id": "def-public.TextAreaWithMessageVariables.$1", "type": "CompoundType", "tags": [], - "label": "{\n messageVariables,\n paramsProperty,\n index,\n inputTargetValue,\n isDisabled = false,\n editAction,\n label,\n errors,\n}", + "label": "{\n messageVariables,\n paramsProperty,\n index,\n inputTargetValue,\n isDisabled = false,\n editAction,\n label,\n errors,\n helpText,\n}", "description": [], "signature": [ "React.PropsWithChildren" diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 9eafa3e8da610..84aba97cc4adc 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 564 | 12 | 538 | 50 | +| 570 | 12 | 544 | 50 | ## Client diff --git a/api_docs/ui_actions.devdocs.json b/api_docs/ui_actions.devdocs.json index 1335ccf655813..466038b9bb162 100644 --- a/api_docs/ui_actions.devdocs.json +++ b/api_docs/ui_actions.devdocs.json @@ -2241,6 +2241,27 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "uiActions", + "id": "def-public.VisualizeFieldContext.textBasedColumns", + "type": "Array", + "tags": [], + "label": "textBasedColumns", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableColumn", + "text": "DatatableColumn" + }, + "[] | undefined" + ], + "path": "src/plugins/ui_actions/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "uiActions", "id": "def-public.VisualizeFieldContext.originatingApp", diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 01c5f50bac220..7cff9e38c01dc 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 144 | 2 | 102 | 9 | +| 145 | 2 | 103 | 9 | ## Client diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 3e22d68385fce..a574e50b1282b 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 3da5d56294975..e38231b65f207 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 03e9d55385d40..578b5f8165084 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-08-08 +date: 2023-08-10 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 153cbbc053047..a352625e3e118 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index aeaf31dc6c99d..00c541219be11 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 8d3fab6cb85b4..c0c067bb5ea27 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-08-08 +date: 2023-08-10 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 a87f8530bd772..48e4afa5a952c 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-08-08 +date: 2023-08-10 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 41ba7d449fded..cec241a512ef1 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-08-08 +date: 2023-08-10 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 a0b85ee1fcbae..0f3ccf018e385 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-08-08 +date: 2023-08-10 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 cf0915fb30f3c..c3b18a05c609f 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-08-08 +date: 2023-08-10 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 b474cae15bc68..75a89ea4a40eb 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-08-08 +date: 2023-08-10 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 5bc15be9f7c0f..5699386716742 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-08-08 +date: 2023-08-10 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 7643ddfaae90e..73ddb8d102d73 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-08-08 +date: 2023-08-10 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 d7fc9723f84a0..4185e8265e10c 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-08-08 +date: 2023-08-10 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 fe7f2cd1a24a0..c7f07b1d5229a 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-08-08 +date: 2023-08-10 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 925862995104a..e58e86f88b3ab 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-08-08 +date: 2023-08-10 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 68339735ba1b9..2e8de9a940af1 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-08-08 +date: 2023-08-10 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 c44837021f0c4..5df7d900f783c 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-08-08 +date: 2023-08-10 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 7b2d1ab51cdeb..aeefc7168a6e5 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-08-08 +date: 2023-08-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/config/serverless.yml b/config/serverless.yml index 43e9b86eb1f4a..7a79f262cea73 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -90,3 +90,6 @@ vis_type_timeseries.readOnly: true vis_type_vislib.readOnly: true vis_type_xy.readOnly: true input_control_vis.readOnly: true + +# Disable cases in stack management +xpack.cases.stack.enabled: false diff --git a/docs/api-generated/rules/rule-apis-passthru.asciidoc b/docs/api-generated/rules/rule-apis-passthru.asciidoc index 1dea766b0dfb3..63aad889c865c 100644 --- a/docs/api-generated/rules/rule-apis-passthru.asciidoc +++ b/docs/api-generated/rules/rule-apis-passthru.asciidoc @@ -3113,14 +3113,14 @@ Any modifications made to this file will be overwritten.

actions_inner - Up

-
+
An action that runs under defined conditions.
alerts_filter (optional)
connector_type_id (optional)
String The type of connector. This property appears in responses but cannot be set in requests.
frequency (optional)
-
group (optional)
String The group name for the actions. If you don't need to group actions, set to default.
-
id (optional)
String The identifier for the connector saved object.
-
params (optional)
map[String, oas_any_type_not_mapped] The parameters for the action, which are sent to the connector. The params are handled as Mustache templates and passed a default set of context.
+
group
String The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names. If you don't need to group actions, set to default.
+
id
String The identifier for the connector saved object.
+
params
map[String, oas_any_type_not_mapped] The parameters for the action, which are sent to the connector. The params are handled as Mustache templates and passed a default set of context.
uuid (optional)
String A universally unique identifier (UUID) for the action.
@@ -3159,7 +3159,7 @@ Any modifications made to this file will be overwritten.

actions_inner_frequency - Up

-
The parameters that affect how often actions are generated. NOTE: You cannot specify these parameters when notify_when or throttle are defined at the rule level.
+
The properties that affect how often actions are generated. If the rule type supports setting summary to true, the action can be a summary of alerts at the specified notification interval. Otherwise, an action runs for each alert at the specified notification interval. NOTE: You cannot specify these parameters when notify_when or throttle are defined at the rule level.
notify_when
summary
Boolean Indicates whether the action is a summary.
diff --git a/docs/user/production-considerations/alerting-production-considerations.asciidoc b/docs/user/production-considerations/alerting-production-considerations.asciidoc index e3d343475e175..59c8a4bfa6d15 100644 --- a/docs/user/production-considerations/alerting-production-considerations.asciidoc +++ b/docs/user/production-considerations/alerting-production-considerations.asciidoc @@ -56,14 +56,10 @@ Predicting the buffer required to account for actions depends heavily on the rul experimental[] -Alerts and actions log activity in a set of "event log" indices. These indices are configured with an index lifecycle management (ILM) policy, which you can customize. The default policy rolls over the index when it reaches 50GB, or after 30 days. Indices over 90 days old are deleted. +Alerts and actions log activity in a set of "event log" data streams, one per Kibana version, named `.kibana-event-log-{VERSION}`. These data streams are configured with a lifecycle data retention of 90 days. This can be updated to other values via the standard data stream lifecycle APIs. Note that the event log data contains the data shown in the alerting pages in {kib}, so reducing the data retention period will result in less data being available to view. -The name of the index policy is `kibana-event-log-policy`. {kib} creates the index policy on startup, if it doesn't already exist. The index policy can be customized for your environment, but {kib} never modifies the index policy after creating it. - -Because {kib} uses the documents to display historic data, you should set the delete phase longer than you would like the historic data to be shown. For example, if you would like to see one month's worth of historic data, you should set the delete phase to at least one month. - -For more information on index lifecycle management, see: -{ref}/index-lifecycle-management.html[Index Lifecycle Policies]. +For more information on data stream lifecycle management, see: +{ref}/data-stream-lifecycle.html[Data stream lifecycle]. [float] [[alerting-circuit-breakers]] diff --git a/examples/bfetch_explorer/public/components/page/index.tsx b/examples/bfetch_explorer/public/components/page/index.tsx index b4ce0806b1356..921f4b5fa1635 100644 --- a/examples/bfetch_explorer/public/components/page/index.tsx +++ b/examples/bfetch_explorer/public/components/page/index.tsx @@ -7,34 +7,23 @@ */ import * as React from 'react'; -import { - EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, - EuiPageHeader, - EuiPageHeaderSection, - EuiTitle, -} from '@elastic/eui'; +import { EuiPageTemplate, EuiPageSection, EuiPageHeader } from '@elastic/eui'; export interface PageProps { title?: React.ReactNode; + sidebar?: React.ReactNode; } -export const Page: React.FC = ({ title = 'Untitled', children }) => { +export const Page: React.FC = ({ title = 'Untitled', sidebar, children }) => { return ( - - - - -

{title}

-
-
-
- - - {children} - - -
+ + {sidebar} + + + + + {children} + + ); }; diff --git a/examples/bfetch_explorer/public/containers/app/index.tsx b/examples/bfetch_explorer/public/containers/app/index.tsx index 54395ff4eb46d..1c6c6c208f7b1 100644 --- a/examples/bfetch_explorer/public/containers/app/index.tsx +++ b/examples/bfetch_explorer/public/containers/app/index.tsx @@ -11,7 +11,6 @@ import { Redirect } from 'react-router-dom'; import { BrowserRouter as Router, Route, Routes } from '@kbn/shared-ux-router'; import { EuiPage } from '@elastic/eui'; import { useDeps } from '../../hooks/use_deps'; -import { Sidebar } from './sidebar'; import { routes } from '../../routes'; export const App: React.FC = () => { @@ -27,7 +26,6 @@ export const App: React.FC = () => { return ( - {routeElements} diff --git a/examples/bfetch_explorer/public/containers/app/pages/page_count_until/index.tsx b/examples/bfetch_explorer/public/containers/app/pages/page_count_until/index.tsx index 75a20904256b5..b0ed74a570bb4 100644 --- a/examples/bfetch_explorer/public/containers/app/pages/page_count_until/index.tsx +++ b/examples/bfetch_explorer/public/containers/app/pages/page_count_until/index.tsx @@ -11,6 +11,7 @@ import { EuiPanel, EuiText } from '@elastic/eui'; import { CountUntil } from '../../../../components/count_until'; import { Page } from '../../../../components/page'; import { useDeps } from '../../../../hooks/use_deps'; +import { Sidebar } from '../../sidebar'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface Props {} @@ -19,7 +20,7 @@ export const PageCountUntil: React.FC = () => { const { plugins } = useDeps(); return ( - + }> This demo sends a single number N using fetchStreaming to the server. The server will stream back N number of messages with 1 second delay each containing a number diff --git a/examples/bfetch_explorer/public/containers/app/pages/page_double_integers/index.tsx b/examples/bfetch_explorer/public/containers/app/pages/page_double_integers/index.tsx index 126e099098cee..0af8218708cbb 100644 --- a/examples/bfetch_explorer/public/containers/app/pages/page_double_integers/index.tsx +++ b/examples/bfetch_explorer/public/containers/app/pages/page_double_integers/index.tsx @@ -11,6 +11,7 @@ import { EuiPanel, EuiText } from '@elastic/eui'; import { DoubleIntegers } from '../../../../components/double_integers'; import { Page } from '../../../../components/page'; import { useDeps } from '../../../../hooks/use_deps'; +import { Sidebar } from '../../sidebar'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface Props {} @@ -19,7 +20,7 @@ export const PageDoubleIntegers: React.FC = () => { const { explorer } = useDeps(); return ( - + }> Below is a list of numbers in milliseconds. They are sent as a batch to the server. For each number server waits given number of milliseconds then doubles the number and streams it diff --git a/examples/bfetch_explorer/public/containers/app/sidebar/index.tsx b/examples/bfetch_explorer/public/containers/app/sidebar/index.tsx index fe0902f88f321..dfd17d18388ae 100644 --- a/examples/bfetch_explorer/public/containers/app/sidebar/index.tsx +++ b/examples/bfetch_explorer/public/containers/app/sidebar/index.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { EuiPageSideBar_Deprecated as EuiPageSideBar, EuiSideNav } from '@elastic/eui'; +import { EuiSideNav } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; import { routes } from '../../../routes'; @@ -18,26 +18,24 @@ export const Sidebar: React.FC = () => { const history = useHistory(); return ( - - ({ - id, - name: title, - isSelected: true, - items: items.map((route) => ({ - id: route.id, - name: route.title, - onClick: () => history.push(`/${route.id}`), - 'data-test-subj': route.id, - })), + ({ + id, + name: title, + isSelected: true, + items: items.map((route) => ({ + id: route.id, + name: route.title, + onClick: () => history.push(`/${route.id}`), + 'data-test-subj': route.id, })), - }, - ]} - /> - + })), + }, + ]} + /> ); }; diff --git a/examples/content_management_examples/kibana.jsonc b/examples/content_management_examples/kibana.jsonc index ae7cd95e4190a..0d4a9b39dc0ef 100644 --- a/examples/content_management_examples/kibana.jsonc +++ b/examples/content_management_examples/kibana.jsonc @@ -12,6 +12,7 @@ "developerExamples", "kibanaReact", "savedObjectsTaggingOss" - ] + ], + "requiredBundles": ["savedObjectsFinder"] } } diff --git a/examples/content_management_examples/public/examples/finder/finder_app.tsx b/examples/content_management_examples/public/examples/finder/finder_app.tsx new file mode 100644 index 0000000000000..e033777216a61 --- /dev/null +++ b/examples/content_management_examples/public/examples/finder/finder_app.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { ContentClientProvider, type ContentClient } from '@kbn/content-management-plugin/public'; +import type { CoreStart } from '@kbn/core/public'; +import { I18nProvider } from '@kbn/i18n-react'; +import { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; + +export const FinderApp = (props: { + contentClient: ContentClient; + core: CoreStart; + savedObjectsTagging: SavedObjectTaggingOssPluginStart; +}) => { + return ( + + + { + alert(JSON.stringify(args)); + }} + savedObjectMetaData={[ + { + type: `search`, + getIconForSavedObject: () => 'discoverApp', + name: 'Saved search', + }, + { + type: 'index-pattern', + getIconForSavedObject: () => 'indexPatternApp', + name: 'Data view', + }, + { + type: `visualization`, + getIconForSavedObject: () => 'visualizeApp', + name: 'Visualization', + }, + { + type: 'lens', + getIconForSavedObject: () => 'lensApp', + name: 'Lens', + }, + { + type: 'map', + getIconForSavedObject: () => 'logoMaps', + name: 'Map', + }, + { + type: 'event-annotation-group', + getIconForSavedObject: () => 'annotation', + name: 'Annotation', + }, + ]} + /> + + + ); +}; diff --git a/src/plugins/saved_objects_finder/server/plugin.test.mocks.ts b/examples/content_management_examples/public/examples/finder/index.tsx similarity index 74% rename from src/plugins/saved_objects_finder/server/plugin.test.mocks.ts rename to examples/content_management_examples/public/examples/finder/index.tsx index af29314a63c79..64809019bbe84 100644 --- a/src/plugins/saved_objects_finder/server/plugin.test.mocks.ts +++ b/examples/content_management_examples/public/examples/finder/index.tsx @@ -6,7 +6,4 @@ * Side Public License, v 1. */ -export const registerRoutesMock = jest.fn(); -jest.doMock('./routes', () => ({ - registerRoutes: registerRoutesMock, -})); +export { FinderApp } from './finder_app'; diff --git a/examples/content_management_examples/public/examples/index.tsx b/examples/content_management_examples/public/examples/index.tsx index dbf1ed1bc01e2..3b92da0ba025e 100644 --- a/examples/content_management_examples/public/examples/index.tsx +++ b/examples/content_management_examples/public/examples/index.tsx @@ -16,6 +16,7 @@ import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { StartDeps } from '../types'; import { TodoApp } from './todos'; import { MSearchApp } from './msearch'; +import { FinderApp } from './finder'; export const renderApp = ( core: CoreStart, @@ -45,6 +46,12 @@ export const renderApp = ( 'data-test-subj': 'msearchExample', href: '/app/contentManagementExamples/msearch', }, + { + id: 'finder', + name: 'Finder', + 'data-test-subj': 'finderExample', + href: '/app/contentManagementExamples/finder', + }, ], }, ]} @@ -64,6 +71,13 @@ export const renderApp = ( savedObjectsTagging={savedObjectsTaggingOss} /> + + + diff --git a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx index a30c652cf0f79..84c5d58b86558 100644 --- a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx +++ b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx @@ -26,14 +26,20 @@ export const MSearchTable = () => { const { hits, pagination } = await contentClient.mSearch({ query: { text: searchQuery, - limit: LISTING_LIMIT, - cursor: '1', tags: { included: refs?.references?.map((ref) => ref.id), excluded: refs?.referencesToExclude?.map((ref) => ref.id), }, }, - contentTypes: [{ contentTypeId: 'map' }], // TODO: improve types to not require objects here? + contentTypes: [ + { contentTypeId: 'map' }, + { contentTypeId: 'dashboard' }, + { contentTypeId: 'visualization' }, + { contentTypeId: 'lens' }, + { contentTypeId: 'search' }, + { contentTypeId: 'index-pattern' }, + { contentTypeId: 'event-annotation-group' }, + ], // TODO: improve types to not require objects here? }); // TODO: needs to have logic of extracting common schema from an unknown mSearch hit: hits.map(hit => cm.convertToCommonSchema(hit)) diff --git a/examples/content_management_examples/tsconfig.json b/examples/content_management_examples/tsconfig.json index a9f20f45792a6..33d00ec0ee82d 100644 --- a/examples/content_management_examples/tsconfig.json +++ b/examples/content_management_examples/tsconfig.json @@ -29,5 +29,6 @@ "@kbn/content-management-table-list-view-table", "@kbn/content-management-table-list-view", "@kbn/shared-ux-router", + "@kbn/saved-objects-finder-plugin", ] } diff --git a/examples/developer_examples/public/app.tsx b/examples/developer_examples/public/app.tsx index 15fa925a0f56b..b95806edb28eb 100644 --- a/examples/developer_examples/public/app.tsx +++ b/examples/developer_examples/public/app.tsx @@ -11,9 +11,9 @@ import ReactDOM from 'react-dom'; import { EuiText, - EuiPageContent_Deprecated as EuiPageContent, + EuiPageTemplate, EuiCard, - EuiPageContentHeader_Deprecated as EuiPageContentHeader, + EuiPageHeader, EuiFlexGroup, EuiFlexItem, EuiFieldSearch, @@ -44,59 +44,66 @@ function DeveloperExamples({ examples, navigateToApp, getUrlForApp }: Props) { }); return ( - - - -

Developer examples

-

- The following examples showcase services and APIs that are available to developers. + <> + + + + + + The following examples showcase services and APIs that are available to developers. + + + setSearch(e.target.value)} isClearable={true} aria-label="Search developer examples" /> -

-
-
- - {filteredExamples.map((def) => ( - - - {def.description} - - } - title={ - - { - navigateToApp(def.appId); - }} - > - - {def.title} - - - - window.open(getUrlForApp(def.appId), '_blank', 'noopener, noreferrer') - } - > - Open in new tab - - - } - image={def.image} - footer={def.links ? : undefined} - /> - ))} - -
+ + + + + {filteredExamples.map((def) => ( + + + {def.description} + + } + title={ + + { + navigateToApp(def.appId); + }} + > + + {def.title} + + + + window.open(getUrlForApp(def.appId), '_blank', 'noopener, noreferrer') + } + > + Open in new tab + + + } + image={def.image} + footer={def.links ? : undefined} + /> + + ))} + + + ); } diff --git a/examples/expressions_explorer/public/actions_and_expressions.tsx b/examples/expressions_explorer/public/actions_and_expressions.tsx index d7322cd34e236..5e118fb4e411c 100644 --- a/examples/expressions_explorer/public/actions_and_expressions.tsx +++ b/examples/expressions_explorer/public/actions_and_expressions.tsx @@ -11,13 +11,14 @@ import { EuiFlexItem, EuiFlexGroup, EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageTemplate, + EuiPageSection, EuiPageHeader, EuiPageHeaderSection, EuiPanel, EuiText, EuiTitle, + EuiSpacer, } from '@elastic/eui'; import { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; @@ -55,8 +56,8 @@ export function ActionsExpressionsExample({ expressions, actions }: Props) { - - + + @@ -67,6 +68,8 @@ export function ActionsExpressionsExample({ expressions, actions }: Props) { + + @@ -86,8 +89,8 @@ export function ActionsExpressionsExample({ expressions, actions }: Props) { - - + + ); } diff --git a/examples/expressions_explorer/public/actions_and_expressions2.tsx b/examples/expressions_explorer/public/actions_and_expressions2.tsx index 10a3c7a679195..4887d358ec731 100644 --- a/examples/expressions_explorer/public/actions_and_expressions2.tsx +++ b/examples/expressions_explorer/public/actions_and_expressions2.tsx @@ -11,13 +11,14 @@ import { EuiFlexItem, EuiFlexGroup, EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageTemplate, + EuiPageSection, EuiPageHeader, EuiPageHeaderSection, EuiPanel, EuiText, EuiTitle, + EuiSpacer, } from '@elastic/eui'; import { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; @@ -54,8 +55,8 @@ export function ActionsExpressionsExample2({ expressions, actions }: Props) { - - + + @@ -65,6 +66,8 @@ export function ActionsExpressionsExample2({ expressions, actions }: Props) { + + @@ -86,8 +89,8 @@ export function ActionsExpressionsExample2({ expressions, actions }: Props) { - - + + ); } diff --git a/examples/expressions_explorer/public/app.tsx b/examples/expressions_explorer/public/app.tsx index 96e8fcb2908bd..776d612fd395d 100644 --- a/examples/expressions_explorer/public/app.tsx +++ b/examples/expressions_explorer/public/app.tsx @@ -12,8 +12,8 @@ import { EuiPage, EuiPageHeader, EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageTemplate, + EuiPageSection, EuiSpacer, EuiText, EuiLink, @@ -55,9 +55,11 @@ const ExpressionsExplorer = ({ - Expressions Explorer - - + + + + +

There are a couple of ways to run the expressions. Below some of the options are @@ -87,8 +89,8 @@ const ExpressionsExplorer = ({ - - + + diff --git a/examples/expressions_explorer/public/render_expressions.tsx b/examples/expressions_explorer/public/render_expressions.tsx index 768aaff133686..7901b456232cb 100644 --- a/examples/expressions_explorer/public/render_expressions.tsx +++ b/examples/expressions_explorer/public/render_expressions.tsx @@ -11,14 +11,15 @@ import { EuiFlexItem, EuiFlexGroup, EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageTemplate, + EuiPageSection, EuiPageHeader, EuiPageHeaderSection, EuiPanel, EuiText, EuiTitle, EuiButton, + EuiSpacer, } from '@elastic/eui'; import { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { Start as InspectorStart } from '@kbn/inspector-plugin/public'; @@ -49,8 +50,8 @@ export function RenderExpressionsExample({ expressions, inspector }: Props) { - - + + @@ -69,6 +70,8 @@ export function RenderExpressionsExample({ expressions, inspector }: Props) { + + @@ -90,8 +93,8 @@ export function RenderExpressionsExample({ expressions, inspector }: Props) { - - + + ); } diff --git a/examples/expressions_explorer/public/run_expressions.tsx b/examples/expressions_explorer/public/run_expressions.tsx index 0b0558568522e..f30069acbc3ca 100644 --- a/examples/expressions_explorer/public/run_expressions.tsx +++ b/examples/expressions_explorer/public/run_expressions.tsx @@ -13,14 +13,15 @@ import { EuiFlexItem, EuiFlexGroup, EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageTemplate, + EuiPageSection, EuiPageHeader, EuiPageHeaderSection, EuiPanel, EuiText, EuiTitle, EuiButton, + EuiSpacer, } from '@elastic/eui'; import { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { Adapters, Start as InspectorStart } from '@kbn/inspector-plugin/public'; @@ -64,8 +65,8 @@ export function RunExpressionsExample({ expressions, inspector }: Props) { - - + + @@ -84,6 +85,8 @@ export function RunExpressionsExample({ expressions, inspector }: Props) { + + @@ -104,8 +107,8 @@ export function RunExpressionsExample({ expressions, inspector }: Props) { - - + + ); } diff --git a/examples/locator_examples/public/app.tsx b/examples/locator_examples/public/app.tsx index c7eb4130b1cba..9531007d811cd 100644 --- a/examples/locator_examples/public/app.tsx +++ b/examples/locator_examples/public/app.tsx @@ -9,13 +9,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { EuiPageBody } from '@elastic/eui'; -import { EuiPageContent_Deprecated as EuiPageContent } from '@elastic/eui'; -import { EuiPageContentBody_Deprecated as EuiPageContentBody } from '@elastic/eui'; +import { EuiPageBody, EuiPageTemplate, EuiPageSection, EuiText } from '@elastic/eui'; import { Redirect, useLocation } from 'react-router-dom'; import { Router, Routes as RouterRoutes, Route } from '@kbn/shared-ux-router'; import { createBrowserHistory } from 'history'; -import { EuiText } from '@elastic/eui'; import { AppMountParameters } from '@kbn/core/public'; function useQuery() { @@ -38,8 +35,8 @@ export const Routes: React.FC<{}> = () => { return ( - - + + = () => { - - + + ); }; diff --git a/examples/locator_explorer/public/app.tsx b/examples/locator_explorer/public/app.tsx index 66bb295891fd6..8e7423e91b93b 100644 --- a/examples/locator_explorer/public/app.tsx +++ b/examples/locator_explorer/public/app.tsx @@ -8,16 +8,18 @@ import React, { useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; -import { EuiPage } from '@elastic/eui'; -import { EuiButton } from '@elastic/eui'; -import { EuiPageBody } from '@elastic/eui'; -import { EuiPageContent_Deprecated as EuiPageContent } from '@elastic/eui'; -import { EuiPageContentBody_Deprecated as EuiPageContentBody } from '@elastic/eui'; -import { EuiSpacer } from '@elastic/eui'; -import { EuiText } from '@elastic/eui'; -import { EuiFieldText } from '@elastic/eui'; -import { EuiPageHeader } from '@elastic/eui'; -import { EuiLink } from '@elastic/eui'; +import { + EuiPage, + EuiButton, + EuiPageBody, + EuiPageTemplate, + EuiPageSection, + EuiSpacer, + EuiText, + EuiFieldText, + EuiPageHeader, + EuiLink, +} from '@elastic/eui'; import { AppMountParameters } from '@kbn/core/public'; import { SharePluginSetup } from '@kbn/share-plugin/public'; import { @@ -112,9 +114,11 @@ const ActionsExplorer = ({ share }: Props) => { return ( - Locator explorer - - + + + + +

Create new links using the most recent version of a locator.

@@ -178,8 +182,8 @@ const ActionsExplorer = ({ share }: Props) => { )) )} -
-
+ +
); diff --git a/examples/locator_explorer/public/page.tsx b/examples/locator_explorer/public/page.tsx index 05d64781c2ea3..18a43a395d67e 100644 --- a/examples/locator_explorer/public/page.tsx +++ b/examples/locator_explorer/public/page.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageTemplate, + EuiPageSection, EuiPageHeader, EuiPageHeaderSection, EuiTitle, @@ -32,9 +32,9 @@ export function Page({ title, children }: PageProps) { - - {children} - + + {children} + ); } diff --git a/examples/screenshot_mode_example/public/components/app.tsx b/examples/screenshot_mode_example/public/components/app.tsx index e3e9917522dfc..d8c4896c7be65 100644 --- a/examples/screenshot_mode_example/public/components/app.tsx +++ b/examples/screenshot_mode_example/public/components/app.tsx @@ -13,9 +13,8 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n-react'; import { EuiPage, EuiPageBody, - EuiPageContent_Deprecated as EuiPageContent, - EuiPageContentBody_Deprecated as EuiPageContentBody, - EuiPageContentHeader_Deprecated as EuiPageContentHeader, + EuiPageTemplate, + EuiPageSection, EuiPageHeader, EuiTitle, EuiText, @@ -82,8 +81,8 @@ export const ScreenshotModeExampleApp = ({ - - + +

{isScreenshotMode ? ( @@ -99,8 +98,8 @@ export const ScreenshotModeExampleApp = ({ )}

-
- + + {isScreenshotMode ? (

We detected screenshot mode. The chrome navbar should be hidden.

@@ -111,8 +110,8 @@ export const ScreenshotModeExampleApp = ({

)}
-
-
+ +
diff --git a/fleet_packages.json b/fleet_packages.json index b736eedd11b49..f93c40d039e6a 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -30,7 +30,7 @@ }, { "name": "elastic_agent", - "version": "1.10.1" + "version": "1.11.0" }, { "name": "endpoint", diff --git a/package.json b/package.json index 98f590001f042..9c9b768f0dc55 100644 --- a/package.json +++ b/package.json @@ -592,6 +592,7 @@ "@kbn/screenshotting-example-plugin": "link:x-pack/examples/screenshotting_example", "@kbn/screenshotting-plugin": "link:x-pack/plugins/screenshotting", "@kbn/search-examples-plugin": "link:examples/search_examples", + "@kbn/search-response-warnings": "link:packages/kbn-search-response-warnings", "@kbn/searchprofiler-plugin": "link:x-pack/plugins/searchprofiler", "@kbn/security-plugin": "link:x-pack/plugins/security", "@kbn/security-solution-ess": "link:x-pack/plugins/security_solution_ess", @@ -837,7 +838,7 @@ "deep-freeze-strict": "^1.1.1", "deepmerge": "^4.2.2", "del": "^6.1.0", - "elastic-apm-node": "^3.49.0", + "elastic-apm-node": "^3.49.1", "email-addresses": "^5.0.0", "execa": "^4.0.2", "expiry-js": "0.1.7", @@ -878,6 +879,7 @@ "js-search": "^1.4.3", "js-sha256": "^0.9.0", "js-yaml": "^3.14.1", + "json-schema-to-ts": "^2.9.1", "json-stable-stringify": "^1.0.1", "json-stringify-pretty-compact": "1.2.0", "json-stringify-safe": "5.0.1", @@ -1394,7 +1396,7 @@ "blob-polyfill": "^7.0.20220408", "callsites": "^3.1.0", "chance": "1.0.18", - "chromedriver": "^114.0.2", + "chromedriver": "^115.0.1", "clean-webpack-plugin": "^3.0.0", "cli-table3": "^0.6.1", "compression-webpack-plugin": "^4.0.0", @@ -1443,6 +1445,7 @@ "faker": "^5.1.0", "fetch-mock": "^7.3.9", "file-loader": "^4.2.0", + "find-cypress-specs": "^1.35.1", "form-data": "^4.0.0", "geckodriver": "^4.0.0", "gulp-brotli": "^3.0.0", @@ -1525,6 +1528,7 @@ "sinon": "^7.4.2", "sort-package-json": "^1.53.1", "source-map": "^0.7.4", + "spec-change": "^1.7.1", "string-replace-loader": "^2.2.0", "style-loader": "^1.1.3", "stylelint": "^14.9.1", diff --git a/packages/core/application/core-application-browser-internal/src/utils/append_app_path.ts b/packages/core/application/core-application-browser-internal/src/utils/append_app_path.ts index 575e1f10a553e..afcdbfb18196a 100644 --- a/packages/core/application/core-application-browser-internal/src/utils/append_app_path.ts +++ b/packages/core/application/core-application-browser-internal/src/utils/append_app_path.ts @@ -8,7 +8,7 @@ import { removeSlashes } from './remove_slashes'; -export const appendAppPath = (appBasePath: string, path: string = '') => { +export const appendAppPath = (appBasePath = '', path: string = '') => { // Only prepend slash if not a hash or query path path = path === '' || path.startsWith('#') || path.startsWith('?') ? path : `/${path}`; // Do not remove trailing slash when in hashbang or basePath diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.tsx index d8e409cad2e09..ad7c6d8fc52a5 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.tsx @@ -158,6 +158,7 @@ describe('start', () => { ] `); }); + it('strips off "snapshot" from the kibana version if present', async () => { const { chrome, service } = await start({ options: { browserSupportsCsp: false, kibanaVersion: '8.0.0-SnAPshot' }, @@ -339,6 +340,24 @@ describe('start', () => { ] `); }); + + it('change visibility when EUI component in full screen', async () => { + const body = document.body; + const startDeps = defaultStartDeps([new FakeApp('foo')], 'foo'); + const { chrome } = await start({ startDeps }); + + // Chrome is initially visible + let isVisible = await Rx.lastValueFrom(chrome.getIsVisible$().pipe(Rx.take(1))); + expect(isVisible).toBe(true); + + // Add EUI class that should hide the chrome + body.classList.add('euiDataGrid__restrictBody'); + await new Promise((resolve) => setTimeout(resolve)); // wait next tick + + // Chrome should be hidden + isVisible = await Rx.lastValueFrom(chrome.getIsVisible$().pipe(Rx.take(1))); + expect(isVisible).toBe(false); + }); }); describe('badge', () => { diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 0b575e4a0f215..76fef465d823c 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -79,6 +79,7 @@ export class ChromeService { private readonly recentlyAccessed = new RecentlyAccessedService(); private readonly docTitle = new DocTitleService(); private readonly projectNavigation = new ProjectNavigationService(); + private mutationObserver: MutationObserver | undefined; constructor(private readonly params: ConstructorParams) {} @@ -114,6 +115,53 @@ export class ChromeService { ); } + private setIsVisible = (isVisible: boolean) => this.isForceHidden$.next(!isVisible); + + /** + * Some EUI component can be toggled in Full screen (e.g. the EuiDataGrid). When they are toggled in full + * screen we want to hide the chrome, and when they are toggled back to normal we want to show the chrome. + */ + private handleEuiFullScreenChanges = () => { + const { body } = document; + // HTML class names that are added to the body when Eui components are toggled in full screen + const classesOnBodyWhenEuiFullScreen = ['euiDataGrid__restrictBody']; + + let isChromeHiddenForEuiFullScreen = false; + let isChromeVisible = false; + + this.isVisible$.pipe(takeUntil(this.stop$)).subscribe((isVisible) => { + isChromeVisible = isVisible; + }); + + const onBodyClassesChange = () => { + const { className } = body; + if ( + classesOnBodyWhenEuiFullScreen.some((name) => className.includes(name)) && + isChromeVisible + ) { + isChromeHiddenForEuiFullScreen = true; + this.setIsVisible(false); + } else if ( + classesOnBodyWhenEuiFullScreen.every((name) => !className.includes(name)) && + !isChromeVisible && + isChromeHiddenForEuiFullScreen + ) { + isChromeHiddenForEuiFullScreen = false; + this.setIsVisible(true); + } + }; + + this.mutationObserver = new MutationObserver((mutationList) => { + mutationList.forEach((mutation) => { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + onBodyClassesChange(); + } + }); + }); + + this.mutationObserver.observe(body, { attributes: true }); + }; + public setup({ analytics }: SetupDeps) { const docTitle = this.docTitle.setup({ document: window.document }); registerAnalyticsContextProvider(analytics, docTitle.title$); @@ -128,6 +176,7 @@ export class ChromeService { customBranding, }: StartDeps): Promise { this.initVisibility(application); + this.handleEuiFullScreenChanges(); const globalHelpExtensionMenuLinks$ = new BehaviorSubject( [] @@ -379,7 +428,7 @@ export class ChromeService { getIsVisible$: () => this.isVisible$, - setIsVisible: (isVisible: boolean) => this.isForceHidden$.next(!isVisible), + setIsVisible: this.setIsVisible.bind(this), getBadge$: () => badge$.pipe(takeUntil(this.stop$)), @@ -463,5 +512,6 @@ export class ChromeService { this.navLinks.stop(); this.projectNavigation.stop(); this.stop$.next(); + this.mutationObserver?.disconnect(); } } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts index ef0c33330a57d..fd1fadff60512 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts @@ -34,13 +34,13 @@ describe('breadcrumbs', () => { const currentLocationPathName = '/foo/item1'; const { projectNavigation, history } = setup({ locationPathName: currentLocationPathName }); - projectNavigation.setProjectNavigation({ + const mockNavigation = { navigationTree: [ { id: 'root', title: 'Root', path: ['root'], - breadcrumbStatus: 'hidden', + breadcrumbStatus: 'hidden' as 'hidden', children: [ { id: 'subNav', @@ -64,8 +64,9 @@ describe('breadcrumbs', () => { ], }, ], - }); - return { projectNavigation, history }; + }; + projectNavigation.setProjectNavigation(mockNavigation); + return { projectNavigation, history, mockNavigation }; }; test('should set breadcrumbs home / nav / custom', async () => { @@ -151,6 +152,42 @@ describe('breadcrumbs', () => { breadcrumbs = await firstValueFrom(projectNavigation.getProjectBreadcrumbs$()); expect(breadcrumbs).toHaveLength(1); // only home is left }); + + // this handles race condition where the final `setProjectNavigation` update happens after the app called `setProjectBreadcrumbs` + test("shouldn't reset initial deep context breadcrumbs", async () => { + const { projectNavigation, mockNavigation } = setupWithNavTree(); + projectNavigation.setProjectNavigation({ navigationTree: [] }); // reset simulating initial state + projectNavigation.setProjectBreadcrumbs([ + { text: 'custom1', href: '/custom1' }, + { text: 'custom2', href: '/custom1/custom2' }, + ]); + projectNavigation.setProjectNavigation(mockNavigation); // restore navigation + + const breadcrumbs = await firstValueFrom(projectNavigation.getProjectBreadcrumbs$()); + expect(breadcrumbs).toHaveLength(4); + }); + + test("shouldn't reset custom breadcrumbs when nav node contents changes, but not the path", async () => { + const { projectNavigation, mockNavigation } = setupWithNavTree(); + projectNavigation.setProjectBreadcrumbs([ + { text: 'custom1', href: '/custom1' }, + { text: 'custom2', href: '/custom1/custom2' }, + ]); + let breadcrumbs = await firstValueFrom(projectNavigation.getProjectBreadcrumbs$()); + expect(breadcrumbs).toHaveLength(4); + + // navigation node contents changed, but not the path + projectNavigation.setProjectNavigation({ + navigationTree: [ + { ...mockNavigation.navigationTree[0], title: 'Changed title' }, + ...mockNavigation.navigationTree, + ], + }); + + // context breadcrumbs should not reset + breadcrumbs = await firstValueFrom(projectNavigation.getProjectBreadcrumbs$()); + expect(breadcrumbs).toHaveLength(4); + }); }); describe('getActiveNodes$()', () => { diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts index 50bf609dde0da..90be8ff754053 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts @@ -16,7 +16,17 @@ import { ChromeProjectNavigationNode, } from '@kbn/core-chrome-browser'; import type { HttpStart } from '@kbn/core-http-browser'; -import { BehaviorSubject, Observable, combineLatest, map, takeUntil, ReplaySubject } from 'rxjs'; +import { + BehaviorSubject, + Observable, + combineLatest, + map, + takeUntil, + ReplaySubject, + skip, + distinctUntilChanged, + skipWhile, +} from 'rxjs'; import type { Location } from 'history'; import deepEqual from 'react-fast-compare'; import classnames from 'classnames'; @@ -55,10 +65,25 @@ export class ProjectNavigationService { this.onHistoryLocationChange(application.history.location); this.unlistenHistory = application.history.listen(this.onHistoryLocationChange.bind(this)); - this.activeNodes$.pipe(takeUntil(this.stop$)).subscribe(() => { - // reset the breadcrumbs when the active nodes change - this.projectBreadcrumbs$.next({ breadcrumbs: [], params: { absolute: false } }); - }); + this.activeNodes$ + .pipe( + takeUntil(this.stop$), + // skip while the project navigation is not set + skipWhile(() => !this.projectNavigation$.getValue()), + // only reset when the active breadcrumb path changes, use ids to get more stable reference + distinctUntilChanged((prevNodes, nextNodes) => + deepEqual( + prevNodes?.[0]?.map((node) => node.id), + nextNodes?.[0]?.map((node) => node.id) + ) + ), + // skip the initial state, we only want to reset the breadcrumbs when the active nodes change + skip(1) + ) + .subscribe(() => { + // reset the breadcrumbs when the active nodes change + this.projectBreadcrumbs$.next({ breadcrumbs: [], params: { absolute: false } }); + }); return { setProjectHome: (homeHref: string) => { diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 748b27fe13dd1..c2c9022b42b2a 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -629,17 +629,6 @@ } } }, - "search": { - "dynamic": false, - "properties": { - "title": { - "type": "text" - }, - "description": { - "type": "text" - } - } - }, "alert": { "dynamic": false, "properties": { @@ -931,6 +920,17 @@ } } }, + "search": { + "dynamic": false, + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + } + } + }, "visualization": { "dynamic": false, "properties": { @@ -1657,6 +1657,9 @@ "auth_type": { "type": "keyword" }, + "connection_type": { + "type": "keyword" + }, "username": { "type": "keyword" }, @@ -1743,6 +1746,12 @@ }, "broker_buffer_size": { "type": "integer" + }, + "required_acks": { + "type": "integer" + }, + "channel_buffer_size": { + "type": "integer" } } }, diff --git a/packages/kbn-content-management-utils/src/saved_object_content_storage.ts b/packages/kbn-content-management-utils/src/saved_object_content_storage.ts index ac8877e5a26da..721ce5e46b690 100644 --- a/packages/kbn-content-management-utils/src/saved_object_content_storage.ts +++ b/packages/kbn-content-management-utils/src/saved_object_content_storage.ts @@ -135,6 +135,7 @@ export interface SOContentStorageConstrutorParams { updateArgsToSoUpdateOptions?: UpdateArgsToSoUpdateOptions; searchArgsToSOFindOptions?: SearchArgsToSOFindOptions; enableMSearch?: boolean; + mSearchAdditionalSearchFields?: string[]; } export abstract class SOContentStorage @@ -153,6 +154,7 @@ export abstract class SOContentStorage searchArgsToSOFindOptions, enableMSearch, allowedSavedObjectAttributes, + mSearchAdditionalSearchFields, }: SOContentStorageConstrutorParams) { this.savedObjectType = savedObjectType; this.cmServicesDefinition = cmServicesDefinition; @@ -166,6 +168,7 @@ export abstract class SOContentStorage if (enableMSearch) { this.mSearch = { savedObjectType: this.savedObjectType, + additionalSearchFields: mSearchAdditionalSearchFields, toItemResult: (ctx: StorageContext, savedObject: SavedObjectsFindResult): Types['Item'] => { const transforms = ctx.utils.getTransforms(this.cmServicesDefinition); @@ -201,6 +204,7 @@ export abstract class SOContentStorage mSearch?: { savedObjectType: string; toItemResult: (ctx: StorageContext, savedObject: SavedObjectsFindResult) => Types['Item']; + additionalSearchFields?: string[]; }; async get(ctx: StorageContext, id: string): Promise { diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 249820124ef7a..7bf1b32195dd5 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -319,6 +319,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { overview: `${KIBANA_DOCS}upgrade-assistant.html`, batchReindex: `${KIBANA_DOCS}batch-start-resume-reindex.html`, remoteReindex: `${ELASTICSEARCH_DOCS}docs-reindex.html#reindex-from-remote`, + reindexWithPipeline: `${ELASTICSEARCH_DOCS}docs-reindex.html#reindex-with-an-ingest-pipeline`, }, rollupJobs: `${KIBANA_DOCS}data-rollups.html`, elasticsearch: { diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index dff8af667ccba..5869005187db6 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -298,6 +298,7 @@ export interface DocLinks { readonly overview: string; readonly batchReindex: string; readonly remoteReindex: string; + readonly reindexWithPipeline: string; }; readonly rollupJobs: string; readonly elasticsearch: Record; diff --git a/packages/kbn-event-annotation-components/components/event_annotation_group_saved_object_finder.tsx b/packages/kbn-event-annotation-components/components/event_annotation_group_saved_object_finder.tsx index 4ccdb6164a5a6..4b1549524874f 100644 --- a/packages/kbn-event-annotation-components/components/event_annotation_group_saved_object_finder.tsx +++ b/packages/kbn-event-annotation-components/components/event_annotation_group_saved_object_finder.tsx @@ -8,12 +8,10 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import type { CoreStart } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import type { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; -import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { ContentClient } from '@kbn/content-management-plugin/public'; import { EuiButton, EuiEmptyPrompt, @@ -24,26 +22,25 @@ import { } from '@elastic/eui'; import { css } from '@emotion/react'; import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-common'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; export const EventAnnotationGroupSavedObjectFinder = ({ - uiSettings, - http, - savedObjectsManagement, fixedPageSize = 10, checkHasAnnotationGroups, onChoose, onCreateNew, + contentClient, + uiSettings, }: { + contentClient: ContentClient; uiSettings: IUiSettingsClient; - http: CoreStart['http']; - savedObjectsManagement: SavedObjectsManagementPluginStart; fixedPageSize?: number; checkHasAnnotationGroups: () => Promise; onChoose: (value: { id: string; type: string; fullName: string; - savedObject: SavedObjectCommon; + savedObject: SavedObjectCommon; }) => void; onCreateNew: () => void; }) => { @@ -115,9 +112,8 @@ export const EventAnnotationGroupSavedObjectFinder = ({ } savedObjectMetaData={savedObjectMetaData} services={{ + contentClient, uiSettings, - http, - savedObjectsManagement, }} /> ); diff --git a/packages/kbn-event-annotation-components/tsconfig.json b/packages/kbn-event-annotation-components/tsconfig.json index 09fa401ec12c7..7a26b61878e2f 100644 --- a/packages/kbn-event-annotation-components/tsconfig.json +++ b/packages/kbn-event-annotation-components/tsconfig.json @@ -29,13 +29,12 @@ "@kbn/content-management-table-list-view-table", "@kbn/content-management-tabbed-table-list-view", "@kbn/event-annotation-common", - "@kbn/core", "@kbn/i18n-react", "@kbn/saved-objects-finder-plugin", - "@kbn/saved-objects-management-plugin", "@kbn/core-notifications-browser-mocks", "@kbn/core-notifications-browser", "@kbn/core-saved-objects-api-browser", "@kbn/expressions-plugin", + "@kbn/content-management-plugin", ] } diff --git a/packages/kbn-event-annotation-components/types.ts b/packages/kbn-event-annotation-components/types.ts index 495e204c1f4aa..6753fd95f1bf0 100644 --- a/packages/kbn-event-annotation-components/types.ts +++ b/packages/kbn-event-annotation-components/types.ts @@ -42,7 +42,7 @@ export interface EventAnnotationServiceType { id: string; type: string; fullName: string; - savedObject: SavedObjectCommon; + savedObject: SavedObjectCommon; }) => void; onCreateNew: () => void; }) => JSX.Element; diff --git a/packages/kbn-io-ts-utils/src/strict_keys_rt/index.ts b/packages/kbn-io-ts-utils/src/strict_keys_rt/index.ts index 8af4d690db0fd..268b9055959c2 100644 --- a/packages/kbn-io-ts-utils/src/strict_keys_rt/index.ts +++ b/packages/kbn-io-ts-utils/src/strict_keys_rt/index.ts @@ -24,7 +24,8 @@ type ParsableType = | t.InterfaceType | MergeType | t.DictionaryType - | t.ArrayType; + | t.ArrayType + | t.AnyType; const tags = [ 'DictionaryType', @@ -35,6 +36,7 @@ const tags = [ 'ExactType', 'UnionType', 'ArrayType', + 'AnyType', ]; function isParsableType(type: t.Mixed): type is ParsableType { @@ -47,6 +49,9 @@ function getHandlingTypes(type: t.Mixed, key: string, value: object): t.Mixed[] } switch (type._tag) { + case 'AnyType': + return [type]; + case 'ArrayType': return [type.type]; diff --git a/packages/kbn-io-ts-utils/src/to_boolean_rt/index.ts b/packages/kbn-io-ts-utils/src/to_boolean_rt/index.ts index eb8277adb281e..bf2ca188e24d6 100644 --- a/packages/kbn-io-ts-utils/src/to_boolean_rt/index.ts +++ b/packages/kbn-io-ts-utils/src/to_boolean_rt/index.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -export const toBooleanRt = new t.Type( +export const toBooleanRt = new t.Type( 'ToBoolean', t.boolean.is, (input) => { diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index b08ff89b5dd09..d63b19f67f783 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -129,7 +129,7 @@ pageLoadAssetSize: snapshotRestore: 79032 spaces: 57868 stackAlerts: 58316 - stackConnectors: 36314 + stackConnectors: 52131 synthetics: 40958 telemetry: 51957 telemetryManagementSection: 38586 diff --git a/packages/kbn-search-response-warnings/README.md b/packages/kbn-search-response-warnings/README.md new file mode 100644 index 0000000000000..527df7dab0f3a --- /dev/null +++ b/packages/kbn-search-response-warnings/README.md @@ -0,0 +1,3 @@ +# @kbn/search-response-warnings + +Components and utils to render warnings which happen when executing search request. For example, shard failures and time outs. diff --git a/packages/kbn-search-response-warnings/index.ts b/packages/kbn-search-response-warnings/index.ts new file mode 100644 index 0000000000000..8ef18d86b9a92 --- /dev/null +++ b/packages/kbn-search-response-warnings/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { SearchResponseInterceptedWarning } from './src/types'; + +export { + SearchResponseWarnings, + type SearchResponseWarningsProps, +} from './src/components/search_response_warnings'; + +export { + getSearchResponseInterceptedWarnings, + removeInterceptedWarningDuplicates, +} from './src/utils/get_search_response_intercepted_warnings'; diff --git a/src/plugins/saved_objects_finder/server/types.ts b/packages/kbn-search-response-warnings/jest.config.js similarity index 72% rename from src/plugins/saved_objects_finder/server/types.ts rename to packages/kbn-search-response-warnings/jest.config.js index e6ec45a63d04f..7b3ab9b639841 100644 --- a/src/plugins/saved_objects_finder/server/types.ts +++ b/packages/kbn-search-response-warnings/jest.config.js @@ -5,6 +5,9 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { IRouter, RequestHandlerContext } from '@kbn/core/server'; -export type SavedObjectsRouter = IRouter; +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-search-response-warnings'], +}; diff --git a/packages/kbn-search-response-warnings/kibana.jsonc b/packages/kbn-search-response-warnings/kibana.jsonc new file mode 100644 index 0000000000000..bf1e5616172f4 --- /dev/null +++ b/packages/kbn-search-response-warnings/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/search-response-warnings", + "owner": "@elastic/kibana-data-discovery" +} diff --git a/packages/kbn-search-response-warnings/package.json b/packages/kbn-search-response-warnings/package.json new file mode 100644 index 0000000000000..69ccd790806aa --- /dev/null +++ b/packages/kbn-search-response-warnings/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/search-response-warnings", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-search-response-warnings/src/__mocks__/search_response_warnings.ts b/packages/kbn-search-response-warnings/src/__mocks__/search_response_warnings.ts new file mode 100644 index 0000000000000..9fe2cf02a1671 --- /dev/null +++ b/packages/kbn-search-response-warnings/src/__mocks__/search_response_warnings.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SearchResponseWarning } from '@kbn/data-plugin/public'; + +export const searchResponseTimeoutWarningMock: SearchResponseWarning = { + type: 'timed_out', + message: 'Data might be incomplete because your request timed out', + reason: undefined, +}; + +export const searchResponseShardFailureWarningMock: SearchResponseWarning = { + type: 'shard_failure', + message: '3 of 4 shards failed', + text: 'The data might be incomplete or wrong.', + reason: { + type: 'illegal_argument_exception', + reason: 'Field [__anonymous_] of type [boolean] does not support custom formats', + }, +}; + +export const searchResponseWarningsMock: SearchResponseWarning[] = [ + searchResponseTimeoutWarningMock, + searchResponseShardFailureWarningMock, + { + type: 'shard_failure', + message: '3 of 4 shards failed', + text: 'The data might be incomplete or wrong.', + reason: { + type: 'query_shard_exception', + reason: + 'failed to create query: [.ds-kibana_sample_data_logs-2023.07.11-000001][0] Testing shard failures!', + }, + }, + { + type: 'shard_failure', + message: '1 of 4 shards failed', + text: 'The data might be incomplete or wrong.', + reason: { + type: 'query_shard_exception', + reason: + 'failed to create query: [.ds-kibana_sample_data_logs-2023.07.11-000001][0] Testing shard failures!', + }, + }, +]; diff --git a/packages/kbn-search-response-warnings/src/components/search_response_warnings/__snapshots__/search_response_warnings.test.tsx.snap b/packages/kbn-search-response-warnings/src/components/search_response_warnings/__snapshots__/search_response_warnings.test.tsx.snap new file mode 100644 index 0000000000000..01f917a0e6dbc --- /dev/null +++ b/packages/kbn-search-response-warnings/src/components/search_response_warnings/__snapshots__/search_response_warnings.test.tsx.snap @@ -0,0 +1,508 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SearchResponseWarnings renders "badge" correctly 1`] = ` +
+
+ + + +
+
+`; + +exports[`SearchResponseWarnings renders "callout" correctly 1`] = ` +
+
    +
  • +
    +

    +

    +
    +
    +
    +
    +
    + Data might be incomplete because your request timed out +
    +
    +
    +
    +
    + +
    +
    +

    +

    +
  • +
  • +
    +

    +

    +
    +
    +
    +
    +
    + + 3 of 4 shards failed + +
    +
    +
    +
    +

    + The data might be incomplete or wrong. +

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

    +

    +
  • +
  • +
    +

    +

    +
    +
    +
    +
    +
    + + 3 of 4 shards failed + +
    +
    +
    +
    +

    + The data might be incomplete or wrong. +

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

    +

    +
  • +
  • +
    +

    +

    +
    +
    +
    +
    +
    + + 1 of 4 shards failed + +
    +
    +
    +
    +

    + The data might be incomplete or wrong. +

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

    +

    +
  • +
+
+`; + +exports[`SearchResponseWarnings renders "empty_prompt" correctly 1`] = ` +
+
+
+ +
+
+
+

+ No results found +

+
+
+
    +
  • +
    +
    +
    + Data might be incomplete because your request timed out +
    +
    +
    +
  • +
  • +
    +
    +
    + + 3 of 4 shards failed + +
    +
    +
    +
    +

    + The data might be incomplete or wrong. +

    +
    +
    +
    + +
    +
    +
  • +
  • +
    +
    +
    + + 3 of 4 shards failed + +
    +
    +
    +
    +

    + The data might be incomplete or wrong. +

    +
    +
    +
    + +
    +
    +
  • +
  • +
    +
    +
    + + 1 of 4 shards failed + +
    +
    +
    +
    +

    + The data might be incomplete or wrong. +

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+
+
+`; diff --git a/packages/kbn-search-response-warnings/src/components/search_response_warnings/index.ts b/packages/kbn-search-response-warnings/src/components/search_response_warnings/index.ts new file mode 100644 index 0000000000000..8a3ed6d05600e --- /dev/null +++ b/packages/kbn-search-response-warnings/src/components/search_response_warnings/index.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 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 { + SearchResponseWarnings, + type SearchResponseWarningsProps, +} from './search_response_warnings'; diff --git a/packages/kbn-search-response-warnings/src/components/search_response_warnings/search_response_warnings.test.tsx b/packages/kbn-search-response-warnings/src/components/search_response_warnings/search_response_warnings.test.tsx new file mode 100644 index 0000000000000..6e3c1b1a0d08d --- /dev/null +++ b/packages/kbn-search-response-warnings/src/components/search_response_warnings/search_response_warnings.test.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { SearchResponseWarnings } from './search_response_warnings'; +import { searchResponseWarningsMock } from '../../__mocks__/search_response_warnings'; + +const interceptedWarnings = searchResponseWarningsMock.map((originalWarning, index) => ({ + originalWarning, + action: originalWarning.type === 'shard_failure' ? : undefined, +})); + +describe('SearchResponseWarnings', () => { + it('renders "callout" correctly', () => { + const component = mountWithIntl( + + ); + expect(component.render()).toMatchSnapshot(); + }); + + it('renders "badge" correctly', () => { + const component = mountWithIntl( + + ); + expect(component.render()).toMatchSnapshot(); + }); + + it('renders "empty_prompt" correctly', () => { + const component = mountWithIntl( + + ); + expect(component.render()).toMatchSnapshot(); + }); +}); diff --git a/packages/kbn-search-response-warnings/src/components/search_response_warnings/search_response_warnings.tsx b/packages/kbn-search-response-warnings/src/components/search_response_warnings/search_response_warnings.tsx new file mode 100644 index 0000000000000..3c92096aa982b --- /dev/null +++ b/packages/kbn-search-response-warnings/src/components/search_response_warnings/search_response_warnings.tsx @@ -0,0 +1,313 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { PropsWithChildren, useEffect, useState } from 'react'; +import { + EuiCallOut, + EuiEmptyPrompt, + EuiText, + EuiTextProps, + EuiFlexGroup, + EuiFlexGroupProps, + EuiFlexItem, + EuiToolTip, + EuiButton, + EuiIcon, + EuiPopover, + useEuiTheme, + useEuiFontSize, + EuiButtonIcon, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; +import type { SearchResponseInterceptedWarning } from '../../types'; + +/** + * SearchResponseWarnings component props + */ +export interface SearchResponseWarningsProps { + /** + * An array of warnings which can have actions + */ + interceptedWarnings?: SearchResponseInterceptedWarning[]; + + /** + * View variant + */ + variant: 'callout' | 'badge' | 'empty_prompt'; + + /** + * Custom data-test-subj value + */ + 'data-test-subj': string; +} + +/** + * SearchResponseWarnings component + * @param interceptedWarnings + * @param variant + * @param dataTestSubj + * @constructor + */ +export const SearchResponseWarnings = ({ + interceptedWarnings, + variant, + 'data-test-subj': dataTestSubj, +}: SearchResponseWarningsProps) => { + const { euiTheme } = useEuiTheme(); + const xsFontSize = useEuiFontSize('xs').fontSize; + const [isCalloutVisibleMap, setIsCalloutVisibleMap] = useState>({}); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + useEffect(() => { + setIsCalloutVisibleMap({}); + }, [interceptedWarnings, setIsCalloutVisibleMap]); + + if (!interceptedWarnings?.length) { + return null; + } + + if (variant === 'callout') { + return ( +
+
    + {interceptedWarnings.map((warning, index) => { + if (isCalloutVisibleMap[index] === false) { + return null; + } + return ( +
  • + + setIsCalloutVisibleMap((prev) => ({ ...prev, [index]: false })) + } + > + + + } + color="warning" + iconType="warning" + size="s" + css={css` + .euiTitle { + display: flex; + align-items: center; + } + `} + data-test-subj={dataTestSubj} + /> +
  • + ); + })} +
+
+ ); + } + + if (variant === 'empty_prompt') { + return ( + + {i18n.translate('searchResponseWarnings.noResultsTitle', { + defaultMessage: 'No results found', + })} + + } + body={ +
    + {interceptedWarnings.map((warning, index) => ( +
  • + +
  • + ))} +
+ } + /> + ); + } + + if (variant === 'badge') { + const warningCount = interceptedWarnings.length; + const buttonLabel = i18n.translate('searchResponseWarnings.badgeButtonLabel', { + defaultMessage: '{warningCount} {warningCount, plural, one {warning} other {warnings}}', + values: { + warningCount, + }, + }); + + return ( + + setIsPopoverOpen(true)} + data-test-subj={`${dataTestSubj}_trigger`} + title={buttonLabel} + css={css` + block-size: ${euiTheme.size.l}; + font-size: ${xsFontSize}; + padding: 0 ${euiTheme.size.xs}; + & > * { + gap: ${euiTheme.size.xs}; + } + `} + > + + {warningCount} + + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + > +
    + {interceptedWarnings.map((warning, index) => ( +
  • + + + + + + + + +
  • + ))} +
+
+ ); + } + + return null; +}; + +function WarningContent({ + warning: { originalWarning, action }, + textSize = 's', + groupStyles, + 'data-test-subj': dataTestSubj, +}: { + warning: SearchResponseInterceptedWarning; + textSize?: EuiTextProps['size']; + groupStyles?: Partial; + 'data-test-subj': string; +}) { + const hasDescription = 'text' in originalWarning; + + return ( + + + + {hasDescription ? {originalWarning.message} : originalWarning.message} + + + {hasDescription ? ( + + +

{originalWarning.text}

+
+
+ ) : null} + {action ? {action} : null} +
+ ); +} + +function CalloutTitleWrapper({ + children, + onCloseCallout, +}: PropsWithChildren<{ onCloseCallout: () => void }>) { + return ( + + {children} + + + + + ); +} diff --git a/packages/kbn-search-response-warnings/src/types.ts b/packages/kbn-search-response-warnings/src/types.ts new file mode 100644 index 0000000000000..a0406a050c3c1 --- /dev/null +++ b/packages/kbn-search-response-warnings/src/types.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ReactNode } from 'react'; +import type { SearchResponseWarning } from '@kbn/data-plugin/public'; + +/** + * Search Response Warning type which also includes an action + */ +export interface SearchResponseInterceptedWarning { + originalWarning: SearchResponseWarning; + action?: ReactNode; +} diff --git a/packages/kbn-search-response-warnings/src/utils/get_search_response_intercepted_warnings.test.tsx b/packages/kbn-search-response-warnings/src/utils/get_search_response_intercepted_warnings.test.tsx new file mode 100644 index 0000000000000..34ae546f42ba6 --- /dev/null +++ b/packages/kbn-search-response-warnings/src/utils/get_search_response_intercepted_warnings.test.tsx @@ -0,0 +1,184 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { RequestAdapter } from '@kbn/inspector-plugin/common'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { coreMock } from '@kbn/core/public/mocks'; +import { + getSearchResponseInterceptedWarnings, + removeInterceptedWarningDuplicates, +} from './get_search_response_intercepted_warnings'; +import { searchResponseWarningsMock } from '../__mocks__/search_response_warnings'; + +const servicesMock = { + data: dataPluginMock.createStartContract(), + theme: coreMock.createStart().theme, +}; + +describe('getSearchResponseInterceptedWarnings', () => { + const adapter = new RequestAdapter(); + + it('should catch warnings correctly', () => { + const services = { + ...servicesMock, + }; + services.data.search.showWarnings = jest.fn((_, callback) => { + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[0], {}); + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[1], {}); + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[2], {}); + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[3], {}); + + // plus duplicates + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[0], {}); + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[1], {}); + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[2], {}); + }); + expect( + getSearchResponseInterceptedWarnings({ + services, + adapter, + options: { + disableShardFailureWarning: true, + }, + }) + ).toMatchInlineSnapshot(` + Array [ + Object { + "action": undefined, + "originalWarning": Object { + "message": "Data might be incomplete because your request timed out", + "reason": undefined, + "type": "timed_out", + }, + }, + Object { + "action": , + "originalWarning": Object { + "message": "3 of 4 shards failed", + "reason": Object { + "reason": "Field [__anonymous_] of type [boolean] does not support custom formats", + "type": "illegal_argument_exception", + }, + "text": "The data might be incomplete or wrong.", + "type": "shard_failure", + }, + }, + Object { + "action": , + "originalWarning": Object { + "message": "3 of 4 shards failed", + "reason": Object { + "reason": "failed to create query: [.ds-kibana_sample_data_logs-2023.07.11-000001][0] Testing shard failures!", + "type": "query_shard_exception", + }, + "text": "The data might be incomplete or wrong.", + "type": "shard_failure", + }, + }, + Object { + "action": , + "originalWarning": Object { + "message": "1 of 4 shards failed", + "reason": Object { + "reason": "failed to create query: [.ds-kibana_sample_data_logs-2023.07.11-000001][0] Testing shard failures!", + "type": "query_shard_exception", + }, + "text": "The data might be incomplete or wrong.", + "type": "shard_failure", + }, + }, + ] + `); + }); + + it('should not catch any warnings if disableShardFailureWarning is false', () => { + const services = { + ...servicesMock, + }; + services.data.search.showWarnings = jest.fn((_, callback) => { + // @ts-expect-error for empty meta + callback?.(searchResponseWarningsMock[0], {}); + }); + expect( + getSearchResponseInterceptedWarnings({ + services, + adapter, + options: { + disableShardFailureWarning: false, + }, + }) + ).toBeUndefined(); + }); +}); + +describe('removeInterceptedWarningDuplicates', () => { + it('should remove duplicates successfully', () => { + const interceptedWarnings = searchResponseWarningsMock.map((originalWarning) => ({ + originalWarning, + })); + + expect(removeInterceptedWarningDuplicates([interceptedWarnings[0]])).toEqual([ + interceptedWarnings[0], + ]); + expect(removeInterceptedWarningDuplicates(interceptedWarnings)).toEqual(interceptedWarnings); + expect( + removeInterceptedWarningDuplicates([...interceptedWarnings, ...interceptedWarnings]) + ).toEqual(interceptedWarnings); + }); + + it('should return undefined if the list is empty', () => { + expect(removeInterceptedWarningDuplicates([])).toBeUndefined(); + expect(removeInterceptedWarningDuplicates(undefined)).toBeUndefined(); + }); +}); diff --git a/packages/kbn-search-response-warnings/src/utils/get_search_response_intercepted_warnings.tsx b/packages/kbn-search-response-warnings/src/utils/get_search_response_intercepted_warnings.tsx new file mode 100644 index 0000000000000..38ad0da2639f7 --- /dev/null +++ b/packages/kbn-search-response-warnings/src/utils/get_search_response_intercepted_warnings.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { uniqBy } from 'lodash'; +import { + type DataPublicPluginStart, + type ShardFailureRequest, + ShardFailureOpenModalButton, +} from '@kbn/data-plugin/public'; +import type { RequestAdapter } from '@kbn/inspector-plugin/common'; +import type { CoreStart } from '@kbn/core-lifecycle-browser'; +import type { SearchResponseInterceptedWarning } from '../types'; + +/** + * Intercepts warnings for a search source request + * @param services + * @param adapter + * @param options + */ +export const getSearchResponseInterceptedWarnings = ({ + services, + adapter, + options, +}: { + services: { + data: DataPublicPluginStart; + theme: CoreStart['theme']; + }; + adapter: RequestAdapter; + options?: { + disableShardFailureWarning?: boolean; + }; +}): SearchResponseInterceptedWarning[] | undefined => { + if (!options?.disableShardFailureWarning) { + return undefined; + } + + const interceptedWarnings: SearchResponseInterceptedWarning[] = []; + + services.data.search.showWarnings(adapter, (warning, meta) => { + const { request, response } = meta; + + interceptedWarnings.push({ + originalWarning: warning, + action: + warning.type === 'shard_failure' && warning.text && warning.message ? ( + ({ + request: request as ShardFailureRequest, + response, + })} + color="primary" + isButtonEmpty={true} + /> + ) : undefined, + }); + return true; // suppress the default behaviour + }); + + return removeInterceptedWarningDuplicates(interceptedWarnings); +}; + +/** + * Removes duplicated warnings + * @param interceptedWarnings + */ +export const removeInterceptedWarningDuplicates = ( + interceptedWarnings: SearchResponseInterceptedWarning[] | undefined +): SearchResponseInterceptedWarning[] | undefined => { + if (!interceptedWarnings?.length) { + return undefined; + } + + const uniqInterceptedWarnings = uniqBy(interceptedWarnings, (interceptedWarning) => + JSON.stringify(interceptedWarning.originalWarning) + ); + + return uniqInterceptedWarnings?.length ? uniqInterceptedWarnings : undefined; +}; diff --git a/packages/kbn-search-response-warnings/tsconfig.json b/packages/kbn-search-response-warnings/tsconfig.json new file mode 100644 index 0000000000000..77bffc521e15f --- /dev/null +++ b/packages/kbn-search-response-warnings/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": ["*.ts", "src/**/*", "__mocks__/**/*.ts"], + "kbn_references": [ + "@kbn/data-plugin", + "@kbn/test-jest-helpers", + "@kbn/i18n", + "@kbn/inspector-plugin", + "@kbn/core", + "@kbn/core-lifecycle-browser", + ], + "exclude": ["target/**/*"] +} diff --git a/packages/kbn-server-route-repository/src/decode_request_params.test.ts b/packages/kbn-server-route-repository/src/decode_request_params.test.ts index 96035bc3e0011..8f7e6f421cd36 100644 --- a/packages/kbn-server-route-repository/src/decode_request_params.test.ts +++ b/packages/kbn-server-route-repository/src/decode_request_params.test.ts @@ -119,4 +119,35 @@ describe('decodeRequestParams', () => { expect(decode()).toEqual({}); }); + + it('allows excess keys in an any type', () => { + const decode = () => { + return decodeRequestParams( + { + params: {}, + query: {}, + body: { + body: { + query: 'foo', + }, + }, + }, + t.type({ + body: t.type({ + body: t.any, + }), + }) + ); + }; + + expect(decode).not.toThrow(); + + expect(decode()).toEqual({ + body: { + body: { + query: 'foo', + }, + }, + }); + }); }); diff --git a/packages/kbn-telemetry-tools/src/schema_ftr_validations/schema_to_config_schema.ts b/packages/kbn-telemetry-tools/src/schema_ftr_validations/schema_to_config_schema.ts index b80a42e101284..5c12c199bbe6d 100644 --- a/packages/kbn-telemetry-tools/src/schema_ftr_validations/schema_to_config_schema.ts +++ b/packages/kbn-telemetry-tools/src/schema_ftr_validations/schema_to_config_schema.ts @@ -59,7 +59,8 @@ function valueSchemaToConfigSchema(value: TelemetrySchemaValue): Type { case 'keyword': case 'text': case 'date': - return schema.string(); + // Some plugins return `null` when there is no value to report + return schema.oneOf([schema.string(), schema.literal(null)]); case 'byte': case 'double': case 'float': diff --git a/packages/kbn-user-profile-components/index.ts b/packages/kbn-user-profile-components/index.ts index 5f8d1db6fcf62..c3f5650c17837 100644 --- a/packages/kbn-user-profile-components/index.ts +++ b/packages/kbn-user-profile-components/index.ts @@ -19,6 +19,17 @@ export { getUserDisplayName } from './src/user_profile'; export type { UserProfile, UserProfileUserInfo, - UserProfileAvatarData, GetUserDisplayNameParams, } from './src/user_profile'; +export type { + UserProfileData, + UserSettingsData, + DarkModeValue, + UserProfileAvatarData, +} from './src/types'; +export { useUpdateUserProfile, type UpdateUserProfileHook } from './src/hooks'; +export { + UserProfilesKibanaProvider, + UserProfilesProvider, + type UserProfilesKibanaDependencies, +} from './src/services'; diff --git a/packages/kbn-user-profile-components/src/hooks/index.ts b/packages/kbn-user-profile-components/src/hooks/index.ts new file mode 100644 index 0000000000000..c3f6c8f3b973f --- /dev/null +++ b/packages/kbn-user-profile-components/src/hooks/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 { useUpdateUserProfile, type UpdateUserProfileHook } from './use_update_user_profile'; diff --git a/x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.test.tsx b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.test.tsx similarity index 52% rename from x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.test.tsx rename to packages/kbn-user-profile-components/src/hooks/use_update_user_profile.test.tsx index 6690e9b6cf946..3f3621b8aae18 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.test.tsx +++ b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.test.tsx @@ -1,58 +1,82 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { act, renderHook } from '@testing-library/react-hooks'; -import { BehaviorSubject, first, lastValueFrom } from 'rxjs'; -import { coreMock } from '@kbn/core/public/mocks'; - -import { getUseUpdateUserProfile } from './use_update_user_profile'; -import { UserProfileAPIClient } from './user_profile_api_client'; +import React from 'react'; +import { act, renderHook, type WrapperComponent } from '@testing-library/react-hooks'; +import { BehaviorSubject, first, lastValueFrom, of } from 'rxjs'; -const { notifications, http } = coreMock.createStart(); -const userProfileApiClient = new UserProfileAPIClient(http); -const useUpdateUserProfile = getUseUpdateUserProfile({ - apiClient: userProfileApiClient, - notifications, -}); +import { coreMock } from '@kbn/core/public/mocks'; -describe('useUpdateUserProfile', () => { - let spy: jest.SpyInstance; +import { useUpdateUserProfile } from './use_update_user_profile'; +import { UserProfilesKibanaProvider } from '../services'; + +const core = coreMock.createStart(); +const security = { + authc: {}, + navControlService: {}, + userProfiles: { + getCurrent: jest.fn(), + bulkGet: jest.fn(), + suggest: jest.fn(), + update: jest.fn(), + userProfile$: of({}), + }, + uiApi: {}, +}; + +const { http, notifications } = core; + +const wrapper: WrapperComponent = ({ children }) => ( + () => () => undefined} + > + {children} + +); + +describe('useUpdateUserProfile() hook', () => { + const updateUserProfiles = jest.fn(); beforeEach(() => { - spy = jest.spyOn(userProfileApiClient, 'update'); + security.userProfiles = { + ...security.userProfiles, + update: updateUserProfiles, + userProfile$: of({}), + }; + + updateUserProfiles.mockReset().mockResolvedValue({}); http.get.mockReset(); http.post.mockReset().mockResolvedValue(undefined); notifications.toasts.addSuccess.mockReset(); }); - afterEach(() => { - spy.mockRestore(); - }); - test('should call the apiClient with the updated user profile data', async () => { - const { result } = renderHook(() => useUpdateUserProfile()); + const { result } = renderHook(() => useUpdateUserProfile(), { wrapper }); const { update } = result.current; await act(async () => { update({ userSettings: { darkMode: 'dark' } }); }); - expect(spy).toHaveBeenCalledWith({ userSettings: { darkMode: 'dark' } }); + expect(updateUserProfiles).toHaveBeenCalledWith({ userSettings: { darkMode: 'dark' } }); }); test('should update the isLoading state while updating', async () => { - const { result, waitForNextUpdate } = renderHook(() => useUpdateUserProfile()); - const { update } = result.current; - const httpPostDone = new BehaviorSubject(false); - - http.post.mockImplementationOnce(async () => { - await lastValueFrom(httpPostDone.pipe(first((v) => v === true))); + const updateDone = new BehaviorSubject(false); + updateUserProfiles.mockImplementationOnce(async () => { + await lastValueFrom(updateDone.pipe(first((v) => v === true))); }); + const { result, waitForNextUpdate } = renderHook(() => useUpdateUserProfile(), { wrapper }); + const { update } = result.current; + expect(result.current.isLoading).toBeFalsy(); await act(async () => { @@ -61,16 +85,18 @@ describe('useUpdateUserProfile', () => { expect(result.current.isLoading).toBeTruthy(); - httpPostDone.next(true); // Resolve the http.post promise + updateDone.next(true); // Resolve the http.post promise await waitForNextUpdate(); expect(result.current.isLoading).toBeFalsy(); }); test('should show a success notification by default', async () => { - const { result } = renderHook(() => useUpdateUserProfile()); + const { result } = renderHook(() => useUpdateUserProfile(), { wrapper }); const { update } = result.current; + expect(notifications.toasts.addSuccess).not.toHaveBeenCalled(); + await act(async () => { await update({ userSettings: { darkMode: 'dark' } }); }); @@ -88,11 +114,7 @@ describe('useUpdateUserProfile', () => { return true; }; - const { result } = renderHook(() => - useUpdateUserProfile({ - pageReloadChecker, - }) - ); + const { result } = renderHook(() => useUpdateUserProfile({ pageReloadChecker }), { wrapper }); const { update } = result.current; await act(async () => { @@ -114,21 +136,17 @@ describe('useUpdateUserProfile', () => { const pageReloadChecker = jest.fn(); const initialValue = { foo: 'bar' }; - http.get.mockReset().mockResolvedValue({ data: initialValue }); - const userProfileApiClient2 = new UserProfileAPIClient(http); - await userProfileApiClient2.getCurrent(); // Sets the initial value of the userProfile$ Observable - - const { result } = renderHook(() => - getUseUpdateUserProfile({ - apiClient: userProfileApiClient2, - notifications, - })({ - pageReloadChecker, - }) - ); + + security.userProfiles = { + ...security.userProfiles, + userProfile$: of(initialValue), + }; + + const { result } = renderHook(() => useUpdateUserProfile({ pageReloadChecker }), { wrapper }); const { update } = result.current; const nextValue = { userSettings: { darkMode: 'light' as const } }; + await act(async () => { await update(nextValue); }); diff --git a/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx new file mode 100644 index 0000000000000..3e015fe92eac5 --- /dev/null +++ b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx @@ -0,0 +1,136 @@ +/* + * Copyright 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 { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useCallback, useRef, useState } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { i18n } from '@kbn/i18n'; + +import type { UserProfileData } from '../types'; +import { useUserProfiles } from '../services'; + +interface Props { + notificationSuccess?: { + /** Flag to indicate if a notification is shown after update. Default: `true` */ + enabled?: boolean; + /** Customize the title of the notification */ + title?: string; + /** Customize the "page reload needed" text of the notification */ + pageReloadText?: string; + }; + /** Predicate to indicate if the update requires a page reload */ + pageReloadChecker?: ( + previsous: UserProfileData | null | undefined, + next: UserProfileData + ) => boolean; +} + +const i18nTexts = { + notificationSuccess: { + title: i18n.translate( + 'userProfileComponents.updateUserProfile.notification.submitSuccessTitle', + { + defaultMessage: 'Profile updated', + } + ), + pageReloadText: i18n.translate( + 'userProfileComponents.updateUserProfile.notification.requiresPageReloadDescription', + { + defaultMessage: 'One or more settings require you to reload the page to take effect.', + } + ), + }, +}; + +export const useUpdateUserProfile = ({ + notificationSuccess = {}, + pageReloadChecker, +}: Props = {}) => { + const { userProfileApiClient, notifySuccess } = useUserProfiles(); + const { userProfile$ } = userProfileApiClient; + const { + enabled: notificationSuccessEnabled = true, + title: notificationTitle = i18nTexts.notificationSuccess.title, + pageReloadText = i18nTexts.notificationSuccess.pageReloadText, + } = notificationSuccess; + const [isLoading, setIsLoading] = useState(false); + const userProfileData = useObservable(userProfile$); + // Keep a snapshot before updating the user profile so we can compare previous and updated values + const userProfileSnapshot = useRef(); + + const showSuccessNotification = useCallback( + ({ isRefreshRequired = false }: { isRefreshRequired?: boolean } = {}) => { + if (isRefreshRequired) { + notifySuccess( + { + title: notificationTitle, + text: ( + + +

{pageReloadText}

+ window.location.reload()} + data-test-subj="windowReloadButton" + > + {i18n.translate( + 'userProfileComponents.updateUserProfile.notification.requiresPageReloadButtonLabel', + { + defaultMessage: 'Reload page', + } + )} + +
+
+ ), + }, + { + durationMs: 1000 * 60 * 5, + } + ); + } else { + notifySuccess({ title: notificationTitle }); + } + }, + [notificationTitle, notifySuccess, pageReloadText] + ); + + const onUserProfileUpdate = useCallback( + (updatedData: UserProfileData) => { + setIsLoading(false); + + if (notificationSuccessEnabled) { + const isRefreshRequired = pageReloadChecker?.(userProfileSnapshot.current, updatedData); + showSuccessNotification({ isRefreshRequired }); + } + }, + [notificationSuccessEnabled, showSuccessNotification, pageReloadChecker] + ); + + const update = useCallback( + (updatedData: D) => { + userProfileSnapshot.current = userProfileData; + setIsLoading(true); + return userProfileApiClient.update(updatedData).then(() => onUserProfileUpdate(updatedData)); + }, + [userProfileApiClient, onUserProfileUpdate, userProfileData] + ); + + return { + /** Update the user profile */ + update, + /** Handler to show a notification after the user profile has been updated */ + showSuccessNotification, + /** The current user profile data */ + userProfileData, + /** Flag to indicate if currently updating */ + isLoading, + }; +}; + +export type UpdateUserProfileHook = typeof useUpdateUserProfile; diff --git a/packages/kbn-user-profile-components/src/services.tsx b/packages/kbn-user-profile-components/src/services.tsx new file mode 100644 index 0000000000000..0e08ca911eb61 --- /dev/null +++ b/packages/kbn-user-profile-components/src/services.tsx @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { FC, useContext } from 'react'; +import type { Observable } from 'rxjs'; +import type { NotificationsStart, ToastOptions } from '@kbn/core-notifications-browser'; +import type { MountPoint } from '@kbn/core-mount-utils-browser'; +import type { ThemeServiceStart } from '@kbn/core-theme-browser'; + +import type { UserProfileAPIClient } from './types'; + +type NotifyFn = ( + data: { title: string; text?: JSX.Element }, + options?: { durationMs?: number } +) => void; + +export interface Services { + userProfileApiClient: UserProfileAPIClient; + notifySuccess: NotifyFn; +} + +const UserProfilesContext = React.createContext(null); + +/** + * Abstract external service Provider. + */ +export const UserProfilesProvider: FC = ({ children, ...services }) => { + return {children}; +}; + +/** + * Kibana-specific service types. + */ +export interface UserProfilesKibanaDependencies { + /** CoreStart contract */ + core: { + notifications: NotificationsStart; + theme: ThemeServiceStart; + }; + security: { + userProfiles: UserProfileAPIClient; + }; + /** + * Handler from the '@kbn/kibana-react-plugin/public' Plugin + * + * ``` + * import { toMountPoint } from '@kbn/kibana-react-plugin/public'; + * ``` + */ + toMountPoint: ( + node: React.ReactNode, + options?: { theme$: Observable<{ readonly darkMode: boolean }> } + ) => MountPoint; +} + +/** + * Kibana-specific Provider that maps to known dependency types. + */ +export const UserProfilesKibanaProvider: FC = ({ + children, + ...services +}) => { + const { + core: { notifications, theme }, + security: { userProfiles: userProfileApiClient }, + toMountPoint, + } = services; + + return ( + { + const toastOptions: ToastOptions = {}; + if (options?.durationMs) { + toastOptions.toastLifeTimeMs = options.durationMs; + } + notifications.toasts.addSuccess( + { + title, + text: text ? toMountPoint(text, { theme$: theme.theme$ }) : undefined, + }, + toastOptions + ); + }} + > + {children} + + ); +}; + +/** + * React hook for accessing pre-wired services. + */ +export function useUserProfiles() { + const context = useContext(UserProfilesContext); + + if (!context) { + throw new Error( + 'UserProfilesContext is missing. Ensure your component or React root is wrapped with or .' + ); + } + + return context; +} diff --git a/packages/kbn-user-profile-components/src/types.ts b/packages/kbn-user-profile-components/src/types.ts new file mode 100644 index 0000000000000..77b6895756bdf --- /dev/null +++ b/packages/kbn-user-profile-components/src/types.ts @@ -0,0 +1,47 @@ +/* + * Copyright 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 { Observable } from 'rxjs'; + +/** + * Avatar stored in user profile. + */ +export interface UserProfileAvatarData { + /** + * Optional initials (two letters) of the user to use as avatar if avatar picture isn't specified. + */ + initials?: string; + /** + * Background color of the avatar when initials are used. + */ + color?: string; + /** + * Base64 data URL for the user avatar image. + */ + imageUrl?: string | null; +} + +export type DarkModeValue = '' | 'dark' | 'light'; + +/** + * User settings stored in the data object of the User Profile + */ +export interface UserSettingsData { + darkMode?: DarkModeValue; +} + +export interface UserProfileData { + avatar?: UserProfileAvatarData; + userSettings?: UserSettingsData; + [key: string]: unknown; +} + +export interface UserProfileAPIClient { + userProfile$: Observable; + update: (data: D) => Promise; +} diff --git a/packages/kbn-user-profile-components/src/user_avatar.tsx b/packages/kbn-user-profile-components/src/user_avatar.tsx index 2413694317c27..1b2a2152f27fc 100644 --- a/packages/kbn-user-profile-components/src/user_avatar.tsx +++ b/packages/kbn-user-profile-components/src/user_avatar.tsx @@ -10,8 +10,9 @@ import type { EuiAvatarProps } from '@elastic/eui'; import { EuiAvatar, useEuiTheme } from '@elastic/eui'; import type { FunctionComponent } from 'react'; import React from 'react'; +import { UserProfileAvatarData } from './types'; -import type { UserProfile, UserProfileUserInfo, UserProfileAvatarData } from './user_profile'; +import type { UserProfile, UserProfileUserInfo } from './user_profile'; import { getUserAvatarColor, getUserAvatarInitials, diff --git a/packages/kbn-user-profile-components/src/user_profile.ts b/packages/kbn-user-profile-components/src/user_profile.ts index 4d0803ad6dbec..965f5e9706e8e 100644 --- a/packages/kbn-user-profile-components/src/user_profile.ts +++ b/packages/kbn-user-profile-components/src/user_profile.ts @@ -7,6 +7,7 @@ */ import { VISUALIZATION_COLORS } from '@elastic/eui'; +import type { UserProfileAvatarData, UserProfileData } from './types'; /** * IMPORTANT: @@ -60,29 +61,6 @@ export interface UserProfileUserInfo { full_name?: string; } -/** - * Placeholder for data stored in user profile. - */ -export type UserProfileData = Record; - -/** - * Avatar stored in user profile. - */ -export interface UserProfileAvatarData { - /** - * Optional initials (two letters) of the user to use as avatar if avatar picture isn't specified. - */ - initials?: string; - /** - * Background color of the avatar when initials are used. - */ - color?: string; - /** - * Base64 data URL for the user avatar image. - */ - imageUrl?: string | null; -} - export const USER_AVATAR_FALLBACK_CODE_POINT = 97; // code point for lowercase "a" export const USER_AVATAR_MAX_INITIALS = 2; diff --git a/packages/kbn-user-profile-components/src/user_tooltip.tsx b/packages/kbn-user-profile-components/src/user_tooltip.tsx index c2678996aebb3..76b7ca1414274 100644 --- a/packages/kbn-user-profile-components/src/user_tooltip.tsx +++ b/packages/kbn-user-profile-components/src/user_tooltip.tsx @@ -11,9 +11,10 @@ import { EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { FunctionComponent } from 'react'; import React from 'react'; -import type { UserProfileUserInfo, UserProfileAvatarData } from './user_profile'; +import type { UserProfileUserInfo } from './user_profile'; import { UserAvatar } from './user_avatar'; import { getUserDisplayName } from './user_profile'; +import { UserProfileAvatarData } from './types'; /** * Props of {@link UserToolTip} component diff --git a/packages/kbn-user-profile-components/tsconfig.json b/packages/kbn-user-profile-components/tsconfig.json index 6f64745691be8..a602ca242d733 100644 --- a/packages/kbn-user-profile-components/tsconfig.json +++ b/packages/kbn-user-profile-components/tsconfig.json @@ -13,7 +13,11 @@ ], "kbn_references": [ "@kbn/i18n", - "@kbn/i18n-react" + "@kbn/i18n-react", + "@kbn/core-notifications-browser", + "@kbn/core", + "@kbn/core-theme-browser", + "@kbn/core-mount-utils-browser", ], "exclude": [ "target/**/*", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index c5ce6a38d9dc9..d0b9d01272b24 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -105,7 +105,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", "ingest-agent-policies": "f11cc19275f4c3e4ee7c5cd6423b6706b21b989d", "ingest-download-sources": "d7edc5e588d9afa61c4b831604582891c54ef1c7", - "ingest-outputs": "bffa0fd93dfdde904d7f5aff77df72d1c35938d9", + "ingest-outputs": "b4e636b13a5d0f89f0400fb67811d4cca4736eb0", "ingest-package-policies": "55816507db0134b8efbe0509e311a91ce7e1c6cc", "ingest_manager_settings": "418311b03c8eda53f5d2ea6f54c1356afaa65511", "inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83", diff --git a/src/dev/ci_setup/setup_env.sh b/src/dev/ci_setup/setup_env.sh index 085f433a2218b..c1942775c88b5 100644 --- a/src/dev/ci_setup/setup_env.sh +++ b/src/dev/ci_setup/setup_env.sh @@ -131,6 +131,7 @@ export GECKODRIVER_CDNURL="https://us-central1-elastic-kibana-184716.cloudfuncti export CHROMEDRIVER_CDNURL="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache" export CHROMEDRIVER_CDNBINARIESURL="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache" export RE2_DOWNLOAD_MIRROR="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache" +export SASS_BINARY_SITE="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-sass" export CYPRESS_DOWNLOAD_MIRROR="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/cypress" export CHECKS_REPORTER_ACTIVE=false diff --git a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts index b59a8ee2c5166..dfeff855f867f 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts @@ -10,7 +10,11 @@ import type { PaletteRegistry } from '@kbn/coloring'; import type { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; -import type { RangeSelectContext, ValueClickContext } from '@kbn/embeddable-plugin/public'; +import type { + RangeSelectContext, + ValueClickContext, + MultiValueClickContext, +} from '@kbn/embeddable-plugin/public'; import type { PersistedState } from '@kbn/visualizations-plugin/public'; import { IInterpreterRenderHandlers } from '@kbn/expressions-plugin/common'; import type { HeatmapExpressionProps } from './expression_functions'; @@ -25,6 +29,11 @@ export interface BrushEvent { data: RangeSelectContext['data']; } +export interface MultiFilterEvent { + name: 'multiFilter'; + data: MultiValueClickContext['data']; +} + export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; export type HeatmapRenderProps = HeatmapExpressionProps & { @@ -35,6 +44,7 @@ export type HeatmapRenderProps = HeatmapExpressionProps & { datatableUtilities: DatatableUtilitiesService; onClickValue: (data: FilterEvent['data']) => void; onSelectRange: (data: BrushEvent['data']) => void; + onClickMultiValue: (data: MultiFilterEvent['data']) => void; paletteService: PaletteRegistry; uiState: PersistedState; interactive: boolean; diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx index 34b4df99c9af4..eef7fd3eb6aba 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx @@ -14,6 +14,8 @@ import { GeometryValue, XYChartSeriesIdentifier, Tooltip, + TooltipAction, + TooltipValue, } from '@elastic/charts'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; @@ -121,6 +123,7 @@ describe('HeatmapComponent', function () { uiState, onClickValue: jest.fn(), onSelectRange: jest.fn(), + onClickMultiValue: jest.fn(), datatableUtilities: createDatatableUtilitiesMock(), paletteService: palettesRegistry, formatFactory: formatService.deserialize, @@ -444,4 +447,67 @@ describe('HeatmapComponent', function () { expect(settingsComponent.prop('ariaUseDefaultSummary')).toEqual(true); }); }); + + describe('tooltip', () => { + it('should not have actions if chart is not interactive', () => { + const component = shallowWithIntl(); + const tooltip = component.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions).toBeUndefined(); + }); + it('should have tooltip actions when the chart is fully configured and interactive', () => { + const component = shallowWithIntl(); + const tooltip = component.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions?.length).toBe(1); + expect(actions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + onSelect: expect.any(Function), + disabled: expect.any(Function), + }), + ]) + ); + }); + + it('selecting correct actions calls a callback with correct filter data', () => { + const component = shallowWithIntl(); + const tooltip = component.find(Tooltip); + const actions = tooltip.prop('actions') as TooltipAction[]; + actions[0].onSelect!( + [ + { + label: 'Dest', + datum: { + x: 'a', + y: 'd', + value: 0, + originalIndex: 0, + }, + } as TooltipValue, + { + label: 'Test', + datum: { + x: 'a', + y: 'd', + value: 0, + originalIndex: 0, + }, + } as TooltipValue, + ], + [] + ); + expect(wrapperProps.onClickMultiValue).toHaveBeenCalledWith({ + data: [ + { + cells: [ + { column: 1, row: 0 }, + { column: 2, row: 0 }, + ], + table: wrapperProps.data, + }, + ], + }); + }); + }); }); diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx index 95856d1bae485..97e5979766089 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx @@ -18,12 +18,13 @@ import { ScaleType, Settings, TooltipType, - TooltipProps, ESFixedIntervalUnit, ESCalendarIntervalUnit, PartialTheme, SettingsProps, Tooltip, + SeriesIdentifier, + TooltipValue, } from '@elastic/charts'; import type { CustomPaletteState } from '@kbn/charts-plugin/public'; import { search } from '@kbn/data-plugin/public'; @@ -36,6 +37,7 @@ import { DEFAULT_LEGEND_SIZE, LegendSizeToPixels, } from '@kbn/visualizations-plugin/common/constants'; +import { i18n } from '@kbn/i18n'; import { DatatableColumn } from '@kbn/expressions-plugin/public'; import { IconChartHeatmap } from '@kbn/chart-icons'; import { getOverridesFor } from '@kbn/chart-expressions-common'; @@ -138,7 +140,7 @@ function computeColorRanges( export const HeatmapComponent: FC = memo( ({ - data, + data: table, args, timeZone, formatFactory, @@ -147,6 +149,7 @@ export const HeatmapComponent: FC = memo( datatableUtilities, onClickValue, onSelectRange, + onClickMultiValue, paletteService, uiState, interactive, @@ -204,8 +207,6 @@ export const HeatmapComponent: FC = memo( }, [renderComplete] ); - - const table = data; const valueAccessor = args.valueAccessor ? getAccessorByDimension(args.valueAccessor, table.columns) : undefined; @@ -221,10 +222,10 @@ export const HeatmapComponent: FC = memo( ? getAccessorByDimension(args.yAccessor, table.columns) : undefined; const splitChartRowAccessor = args.splitRowAccessor - ? getSplitDimensionAccessor(data.columns, args.splitRowAccessor, formatFactory) + ? getSplitDimensionAccessor(table.columns, args.splitRowAccessor, formatFactory) : undefined; const splitChartColumnAccessor = args.splitColumnAccessor - ? getSplitDimensionAccessor(data.columns, args.splitColumnAccessor, formatFactory) + ? getSplitDimensionAccessor(table.columns, args.splitColumnAccessor, formatFactory) : undefined; const xAxisColumnIndex = table.columns.findIndex((v) => v.id === xAccessor); @@ -260,6 +261,8 @@ export const HeatmapComponent: FC = memo( datatables: [formattedTable.table], }); + const hasTooltipActions = interactive; + const onElementClick = useCallback( (e: HeatmapElementEvent[]) => { const cell = e[0][0]; @@ -277,6 +280,7 @@ export const HeatmapComponent: FC = memo( }), column: xAxisColumnIndex, value: x, + table, }, ...(yAxisColumn ? [ @@ -290,6 +294,7 @@ export const HeatmapComponent: FC = memo( }), column: yAxisColumnIndex, value: y, + table, }, ] : []), @@ -317,15 +322,10 @@ export const HeatmapComponent: FC = memo( points.push(point); } } - const context: FilterEvent['data'] = { - data: points.map((point) => ({ - row: point.row, - column: point.column, - value: point.value, - table, - })), - }; - onClickValue(context); + + onClickValue({ + data: points, + }); }, [ args.splitColumnAccessor, @@ -472,10 +472,6 @@ export const HeatmapComponent: FC = memo( } } - const tooltip: TooltipProps = { - type: args.showTooltip ? TooltipType.Follow : TooltipType.None, - }; - const valueFormatter = (d: number) => { let value = d; @@ -598,6 +594,69 @@ export const HeatmapComponent: FC = memo( const xAxisTitle = args.gridConfig.xTitle ?? xAxisColumn?.name; const yAxisTitle = args.gridConfig.yTitle ?? yAxisColumn?.name; + const filterSelectedTooltipValues = ( + tooltipSelectedValues: Array< + TooltipValue, SeriesIdentifier> + > + ) => { + const { datum } = tooltipSelectedValues[0]; + if (!datum) { + return; + } + const { x, y } = datum; + + const shouldFilterByX = tooltipSelectedValues.some( + ({ label }) => label === xAxisColumn?.name + ); + + const shouldFilterByY = tooltipSelectedValues.some( + ({ label }) => label === yAxisColumn?.name + ); + + const cells = [ + ...(xAxisColumn && shouldFilterByX + ? [ + { + column: xAxisColumnIndex, + row: table.rows.findIndex((r) => { + if (!xAxisColumn) return false; + if (formattedTable.formattedColumns[xAxisColumn.id]) { + // stringify the value to compare with the chart value + return xValuesFormatter.convert(r[xAxisColumn.id]) === x; + } + return r[xAxisColumn.id] === x; + }), + }, + ] + : []), + ...(yAxisColumn && shouldFilterByY + ? [ + { + column: yAxisColumnIndex, + row: table.rows.findIndex((r) => { + if (formattedTable.formattedColumns[yAxisColumn.id]) { + // stringify the value to compare with the chart value + return yValuesFormatter.convert(r[yAxisColumn.id]) === y; + } + return r[yAxisColumn.id] === y; + }), + }, + ] + : []), + ]; + + if (cells.length) { + onClickMultiValue({ + data: [ + { + table, + cells, + }, + ], + }); + } + }; + return ( <> {showLegend !== undefined && ( @@ -619,7 +678,31 @@ export const HeatmapComponent: FC = memo( splitColumnAccessor={splitChartColumnAccessor} splitRowAccessor={splitChartRowAccessor} /> - + , SeriesIdentifier> + actions={ + hasTooltipActions + ? [ + { + disabled: (selected) => selected.length < 1, + label: (selected) => + selected.length === 0 + ? i18n.translate( + 'expressionHeatmap.tooltipActions.emptyFilterSelection', + { + defaultMessage: 'Select at least one series to filter', + } + ) + : i18n.translate('expressionHeatmap.tooltipActions.filterValues', { + defaultMessage: 'Filter {seriesNumber} series', + values: { seriesNumber: selected.length }, + }), + onSelect: filterSelectedTooltipValues, + }, + ] + : undefined + } + type={args.showTooltip ? TooltipType.Follow : TooltipType.None} + /> { handlers.event({ name: 'brush', data }); }; + const onClickMultiValue = (data: MultiFilterEvent['data']) => { + handlers.event({ name: 'multiFilter', data }); + }; const renderComplete = () => { const executionContext = handlers.getExecutionContext(); @@ -97,6 +101,7 @@ export const heatmapRenderer: ( chartsActiveCursorService={plugins.charts.activeCursor} syncTooltips={config.syncTooltips} syncCursor={config.syncCursor} + onClickMultiValue={onClickMultiValue} />
diff --git a/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.test.ts b/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.test.ts index 6b510b28c8060..6a4912f0af501 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.test.ts +++ b/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.test.ts @@ -136,7 +136,7 @@ describe('createSplitPoint', () => { const point = createSplitPoint(splitDimension, 'c', defaultFormatter, data); expect(defaultFormatter).toHaveBeenCalledTimes(1); - expect(point).toStrictEqual({ column: 2, row: 1, value: 'c' }); + expect(point).toStrictEqual({ column: 2, row: 1, value: 'c', table: data }); }); it('returns undefined if value is not found in the table', () => { diff --git a/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.ts b/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.ts index e59d6c1aec28b..ec4c69ad233c3 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.ts +++ b/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.ts @@ -51,6 +51,7 @@ export function createSplitPoint( row: splitPointRowIndex, column: table.columns.findIndex((column) => column.id === accessor), value: table.rows[splitPointRowIndex][accessor], + table, }; } } diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 65fa256304fde..0b8a82459fd97 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -223,6 +223,15 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` > { + it('should not have actions if chart is not interactive', () => { + const component = shallow(); + const tooltip = component.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions).toBeUndefined(); + }); + it('should not have actions if chart has only metrics', () => { + const noBucketParams = { + ...wrapperProps, + visParams: { + ...wrapperProps.visParams, + dimensions: { ...wrapperProps.visParams.dimensions, buckets: [] }, + }, + }; + + const component = shallow(); + const tooltip = component.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions).toBeUndefined(); + }); + it('should have tooltip actions when the chart is fully configured and interactive', () => { + const component = shallow(); + const tooltip = component.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions?.length).toBe(1); + expect(actions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + onSelect: expect.any(Function), + disabled: expect.any(Function), + }), + ]) + ); + }); + it('selecting correct actions calls a callback with correct filter data', () => { + const component = shallow(); + const tooltip = component.find(Tooltip); + const actions = tooltip.prop('actions') as TooltipAction[]; + actions[0].onSelect!( + [ + { + label: 'JetBeats', + color: '#79aad9', + isHighlighted: false, + isVisible: true, + seriesIdentifier: { + specId: 'donut', + key: 'JetBeats', + }, + value: 655, + formattedValue: '655', + valueAccessor: 1, + }, + ], + [] + ); + expect(wrapperProps.fireEvent).toHaveBeenCalledWith({ + name: 'multiFilter', + data: { data: [{ cells: [{ column: 0, row: 2 }], table: wrapperProps.visData }] }, + }); + }); + }); }); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index c151741158ac1..de1c977cbdadf 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -20,7 +20,9 @@ import { PartitionElementEvent, SettingsProps, Tooltip, + TooltipValue, } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; import { useEuiTheme } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; import { LegendToggle, ChartsPluginSetup } from '@kbn/charts-plugin/public'; @@ -72,6 +74,7 @@ import { } from './partition_vis_component.styles'; import { filterOutConfig } from '../utils/filter_out_config'; import { ColumnCellValueActions, FilterEvent, StartDeps } from '../types'; +import { getMultiFilterCells } from '../utils/filter_helpers'; declare global { interface Window { @@ -205,6 +208,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { splitChartDimension, splitChartFormatter ); + props.fireEvent({ name: 'filter', data: { data } }); }, [metricColumn.id, originalVisData, props, visParams.dimensions.metrics.length] @@ -385,10 +389,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { ); const fixedViewPort = document.getElementById('app-fixed-viewport'); - const tooltip: TooltipProps = { - ...(fixedViewPort ? { boundary: fixedViewPort } : {}), - type: visParams.addTooltip ? TooltipType.Follow : TooltipType.None, - }; + const legendPosition = visParams.legendPosition ?? Position.Right; const splitChartColumnAccessor = splitColumn @@ -405,6 +406,49 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { ? getColumnByAccessor(splitRow[0], visData.columns) : undefined; + const hasTooltipActions = + interactive && bucketAccessors.filter((a) => a !== 'metric-name').length > 0; + + const tooltip: TooltipProps = { + ...(fixedViewPort ? { boundary: fixedViewPort } : {}), + type: visParams.addTooltip ? TooltipType.Follow : TooltipType.None, + actions: hasTooltipActions + ? [ + { + disabled: (selected) => selected.length < 1, + label: (selected) => + selected.length === 0 + ? i18n.translate('expressionPartitionVis.tooltipActions.emptyFilterSelection', { + defaultMessage: 'Select at least one series to filter', + }) + : i18n.translate('expressionPartitionVis.tooltipActions.filterValues', { + defaultMessage: 'Filter {seriesNumber} series', + values: { seriesNumber: selected.length }, + }), + onSelect: ( + tooltipSelectedValues: Array< + TooltipValue, SeriesIdentifier> + > + ) => { + const cells = getMultiFilterCells(tooltipSelectedValues, bucketColumns, visData); + + props.fireEvent({ + name: 'multiFilter', + data: { + data: [ + { + table: visData, + cells, + }, + ], + }, + }); + }, + }, + ] + : undefined, + }; + /** * Checks whether data have all zero values. * If so, the no data container is loaded. diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/types.ts b/src/plugins/chart_expressions/expression_partition_vis/public/types.ts index 77636d3a41832..0beca2e79de8d 100755 --- a/src/plugins/chart_expressions/expression_partition_vis/public/types.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/types.ts @@ -5,7 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { CellValueContext, ValueClickContext } from '@kbn/embeddable-plugin/public'; +import type { + CellValueContext, + ValueClickContext, + MultiValueClickContext, +} from '@kbn/embeddable-plugin/public'; import { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public'; import { Plugin as ExpressionsPublicPlugin, @@ -36,6 +40,11 @@ export interface FilterEvent { data: ValueClickContext['data']; } +export interface MultiFilterEvent { + name: 'multiFilter'; + data: MultiValueClickContext['data']; +} + export interface CellValueAction { id: string; iconType: string; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index c7b793d87149f..b34a4f74147d8 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { LayerValue, SeriesIdentifier } from '@elastic/charts'; +import { LayerValue, SeriesIdentifier, TooltipValue } from '@elastic/charts'; import { Datatable, DatatableColumn } from '@kbn/expressions-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { ValueClickContext } from '@kbn/embeddable-plugin/public'; @@ -26,6 +26,45 @@ export const canFilter = async ( return Boolean(filters.length); }; +export const getMultiFilterCells = ( + tooltipSelectedValues: Array, SeriesIdentifier>>, + bucketColumns: Array>, + visData: Datatable +) => { + const row = visData.rows.findIndex((r) => + tooltipSelectedValues.every(({ valueAccessor, seriesIdentifier }) => { + if (typeof valueAccessor !== 'number' || valueAccessor < 1) return; + const index = valueAccessor - 1; + const bucketColumnId = bucketColumns[index].id; + if (!bucketColumnId) return; + return r[bucketColumnId] === seriesIdentifier.key; + }) + ); + + return tooltipSelectedValues + .map(({ valueAccessor }) => { + if (typeof valueAccessor !== 'number' || valueAccessor < 1) return; + const index = valueAccessor - 1; + const bucketColumnId = bucketColumns[index].id; + if (!bucketColumnId) return; + const column = visData.columns.findIndex((c) => c.id === bucketColumnId); + + if (column === -1) { + return; + } + + return { + column, + row, + }; + }) + .filter(nonNullable); +}; + +function nonNullable(v: T): v is NonNullable { + return v != null; +} + export const getFilterClickData = ( clickedLayers: LayerValue[], bucketColumns: Array>, diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_legend_actions.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_legend_actions.tsx index b14bf88edf1f8..778c2003de4f1 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_legend_actions.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_legend_actions.tsx @@ -64,7 +64,7 @@ export const getLegendActions = ( panelItems.push( { name: i18n.translate('expressionPartitionVis.legend.filterForValueButtonAriaLabel', { - defaultMessage: 'Filter for value', + defaultMessage: 'Filter for', }), 'data-test-subj': `legend-${title}-filterIn`, icon: , @@ -75,7 +75,7 @@ export const getLegendActions = ( }, { name: i18n.translate('expressionPartitionVis.legend.filterOutValueButtonAriaLabel', { - defaultMessage: 'Filter out value', + defaultMessage: 'Filter out', }), 'data-test-subj': `legend-${title}-filterOut`, icon: , diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index fb8faf123fa66..fe76259b65889 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -565,6 +565,11 @@ exports[`XYChart component it renders area 1`] = ` "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -1568,6 +1573,11 @@ exports[`XYChart component it renders bar 1`] = ` "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -2571,6 +2581,11 @@ exports[`XYChart component it renders horizontal bar 1`] = ` "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -3574,6 +3589,11 @@ exports[`XYChart component it renders line 1`] = ` "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -4577,6 +4597,11 @@ exports[`XYChart component it renders stacked area 1`] = ` "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -5580,6 +5605,11 @@ exports[`XYChart component it renders stacked bar 1`] = ` "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -6583,6 +6613,11 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -7616,6 +7651,11 @@ exports[`XYChart component split chart should render split chart if both, splitR "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -8857,6 +8897,11 @@ exports[`XYChart component split chart should render split chart if splitColumnA "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} @@ -10091,6 +10136,11 @@ exports[`XYChart component split chart should render split chart if splitRowAcce "label": [Function], "onSelect": [Function], }, + Object { + "disabled": [Function], + "label": [Function], + "onSelect": [Function], + }, ] } headerFormatter={[Function]} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx index 1a6fae2feb153..9fa488db24357 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx @@ -56,7 +56,7 @@ export const LegendActionPopover: React.FunctionComponent, @@ -67,7 +67,7 @@ export const LegendActionPopover: React.FunctionComponent, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip_actions.tsx b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip_actions.tsx new file mode 100644 index 0000000000000..04cec59fd0b1d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip_actions.tsx @@ -0,0 +1,259 @@ +/* + * Copyright 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 { Datum, TooltipAction, TooltipValue, XYChartSeriesIdentifier } from '@elastic/charts'; +import { + getAccessorByDimension, + getColumnByAccessor, +} from '@kbn/visualizations-plugin/common/utils'; +import { FormatFactory } from '@kbn/visualization-ui-components'; +import { FieldFormat } from '@kbn/field-formats-plugin/common'; +import { i18n } from '@kbn/i18n'; +import { MultiClickTriggerEvent } from '@kbn/charts-plugin/public'; +import { Datatable } from '@kbn/expressions-plugin/common'; +import { BooleanRelation } from '@kbn/es-query'; +import { isTimeChart } from '../../../common/helpers'; +import { CommonXYDataLayerConfig } from '../../../common'; +import { DatatablesWithFormatInfo, LayersFieldFormats } from '../../helpers'; +import { MultiFilterEvent } from '../../types'; + +type XYTooltipValue = TooltipValue, XYChartSeriesIdentifier>; + +function nonNullable(v: T): v is NonNullable { + return v != null; +} + +export function createSplitPoint( + accessor: string | number, + value: string | number | undefined, + rows: Datatable['rows'], + table: Datatable +) { + if (value === undefined) return; + const splitPointRowIndex = rows.findIndex((row) => { + if (Array.isArray(value)) { + return value.includes(row[accessor]); + } + return row[accessor] === value; + }); + if (splitPointRowIndex !== -1) { + return { + row: splitPointRowIndex, + column: table.columns.findIndex((column) => column.id === accessor), + value: table.rows[splitPointRowIndex][accessor], + table, + }; + } +} + +export const getXSeriesPoint = ( + layer: CommonXYDataLayerConfig, + value: any, + fieldFormats: LayersFieldFormats, + formattedDatatables: DatatablesWithFormatInfo, + xAxisFormatter: FieldFormat, + formatFactory: FormatFactory +) => { + const { table } = layer; + const xColumn = layer.xAccessor && getColumnByAccessor(layer.xAccessor, table.columns); + const xAccessor = layer.xAccessor + ? getAccessorByDimension(layer.xAccessor, table.columns) + : undefined; + + const xFormat = xColumn ? fieldFormats[layer.layerId].xAccessors[xColumn.id] : undefined; + const currentXFormatter = + xAccessor && formattedDatatables[layer.layerId]?.formattedColumns[xAccessor] && xColumn + ? formatFactory(xFormat) + : xAxisFormatter; + + const rowIndex = table.rows.findIndex((row) => { + if (xAccessor) { + if (formattedDatatables[layer.layerId]?.formattedColumns[xAccessor]) { + // stringify the value to compare with the chart value + return currentXFormatter.convert(row[xAccessor]) === value; + } + return row[xAccessor] === value; + } + }); + + return { + row: rowIndex, + column: table.columns.findIndex((col) => col.id === xAccessor), + table, + value: xAccessor ? table.rows[rowIndex][xAccessor] : value, + }; +}; + +function getXSeriesValue(dataLayers: CommonXYDataLayerConfig[], firstSeries: XYTooltipValue) { + const layer = dataLayers.find((l) => + firstSeries.seriesIdentifier.seriesKeys.some((key: string | number) => + l.accessors.some( + (accessor) => getAccessorByDimension(accessor, l.table.columns) === key.toString() + ) + ) + ); + if (!layer) return; + + const { table } = layer; + + const xAccessor = layer.xAccessor + ? getAccessorByDimension(layer.xAccessor, table.columns) + : undefined; + + return xAccessor ? firstSeries.datum?.[xAccessor] : null; +} + +export const getTooltipActions = ( + dataLayers: CommonXYDataLayerConfig[], + onClickMultiValue: (data: MultiFilterEvent['data']) => void, + fieldFormats: LayersFieldFormats, + formattedDatatables: DatatablesWithFormatInfo, + xAxisFormatter: FieldFormat, + formatFactory: FormatFactory, + isEnabled?: boolean +) => { + if (!isEnabled) return; + const hasSplitAccessors = dataLayers.some((l) => l.splitAccessors?.length); + const hasXAxis = dataLayers.every((l) => l.xAccessor); + const isTimeViz = isTimeChart(dataLayers); + + if (!hasSplitAccessors && !hasXAxis) return; + + const xSeriesActions: Array> = hasXAxis + ? [ + { + disabled: () => !hasXAxis, + label: (_, [firstSeries]: XYTooltipValue[]) => { + if (isTimeViz) { + return i18n.translate('expressionXY.tooltipActions.filterByTime', { + defaultMessage: 'Filter by time', + }); + } + + const value = getXSeriesValue(dataLayers, firstSeries); + + return i18n.translate('expressionXY.tooltipActions.filterForXSeries', { + defaultMessage: 'Filter for {value}', + values: { + value: xAxisFormatter.convert(value) || value, + }, + }); + }, + + onSelect: (_: XYTooltipValue[], [firstSeries]: XYTooltipValue[]) => { + const layer = dataLayers.find((l) => + firstSeries.seriesIdentifier.seriesKeys.some((key: string | number) => + l.accessors.some( + (accessor) => getAccessorByDimension(accessor, l.table.columns) === key.toString() + ) + ) + ); + if (!layer) return; + + const value = getXSeriesValue(dataLayers, firstSeries); + + const xSeriesPoint = getXSeriesPoint( + layer, + value, + fieldFormats, + formattedDatatables, + xAxisFormatter, + formatFactory + ); + + const context: MultiFilterEvent['data'] = { + data: [ + { + table: xSeriesPoint.table, + cells: [ + { + row: xSeriesPoint.row, + column: xSeriesPoint.column, + }, + ], + }, + ], + }; + onClickMultiValue(context); + }, + }, + ] + : []; + + const breakdownTooltipActions: Array> = + hasSplitAccessors + ? [ + { + disabled: (selected) => selected.length < 1, + label: (selected) => + selected.length === 0 + ? i18n.translate('expressionXY.tooltipActions.emptyFilterSelection', { + defaultMessage: 'Select at least one series to filter', + }) + : i18n.translate('expressionXY.tooltipActions.filterValues', { + defaultMessage: 'Filter {seriesNumber} selected series', + values: { seriesNumber: selected.length }, + }), + onSelect: (tooltipSelectedValues: XYTooltipValue[]) => { + const layerIndexes: number[] = []; + tooltipSelectedValues.forEach((v) => { + const index = dataLayers.findIndex((l) => + v.seriesIdentifier.seriesKeys.some((key: string | number) => + l.accessors.some( + (accessor) => + getAccessorByDimension(accessor, l.table.columns) === key.toString() + ) + ) + ); + if (!layerIndexes.includes(index) && index !== -1) { + layerIndexes.push(index); + } + }); + + const filterPoints: MultiClickTriggerEvent['data']['data'] = []; + + if (!layerIndexes.length) return; + layerIndexes.forEach((layerIndex) => { + const layer = dataLayers[layerIndex]; + const { table } = layer; + + if (layer.splitAccessors?.length !== 1) return; + + const splitAccessor = getAccessorByDimension( + layer.splitAccessors[0], + table.columns + ); + const splitPoints = tooltipSelectedValues + .map((v) => + createSplitPoint( + splitAccessor, + v.datum?.[splitAccessor], + formattedDatatables[layer.layerId].table.rows, + table + ) + ) + .filter(nonNullable); + if (splitPoints.length) { + filterPoints.push({ + cells: splitPoints.map(({ row, column }) => ({ row, column })), + relation: BooleanRelation.OR, + table, + }); + } + }); + if (filterPoints?.length) { + onClickMultiValue({ + data: filterPoints, + }); + } + }, + }, + ] + : []; + return [...xSeriesActions, ...breakdownTooltipActions]; +}; 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 a43e4f1c05410..4a9e63c5ad7b3 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 @@ -107,6 +107,7 @@ describe('XYChart component', () => { convertSpy = jest.fn((x) => x); getFormatSpy = jest.fn(); getFormatSpy.mockReturnValue({ convert: convertSpy }); + jest.clearAllMocks(); defaultProps = { data: dataPluginMock.createStartContract(), @@ -1140,126 +1141,6 @@ describe('XYChart component', () => { expect(headerFormatter).toBeUndefined(); }); - test('should not have tooltip actions for the detailed tooltip', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - const tooltip = wrapper.find(Tooltip); - const actions = tooltip.prop('actions'); - expect(actions).toBeUndefined(); - }); - - test('should not have tooltip actions for no split accessor', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - const tooltip = wrapper.find(Tooltip); - const actions = tooltip.prop('actions'); - expect(actions).toBeUndefined(); - }); - - test('should have tooltip actions for split accessor and default tooltip', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - const tooltip = wrapper.find(Tooltip); - const actions = tooltip.prop('actions'); - expect(actions?.length).toBe(1); - expect(actions).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - onSelect: expect.any(Function), - disabled: expect.any(Function), - }), - ]) - ); - }); - test('onElementClick returns correct context data', () => { const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null, datum: {} }; const series = { @@ -3548,4 +3429,213 @@ describe('XYChart component', () => { } }); }); + + describe('tooltip actions', () => { + test('should not have tooltip actions for the detailed tooltip', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + const tooltip = wrapper.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions).toBeUndefined(); + }); + test('should not have tooltip action when there is no split accessor nor x serie', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + const tooltip = wrapper.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions).toBeUndefined(); + }); + test('should not have only x series tooltip action when there is no split accessor', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + const tooltip = wrapper.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions?.length).toBe(1); + expect(actions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + onSelect: expect.any(Function), + disabled: expect.any(Function), + }), + ]) + ); + }); + test('should have tooltip actions for split accessor and x series', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + const tooltip = wrapper.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions?.length).toBe(2); + expect(actions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + onSelect: expect.any(Function), + disabled: expect.any(Function), + }), + ]) + ); + }); + test('should have tooltip actions for split accessor', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + const tooltip = wrapper.find(Tooltip); + const actions = tooltip.prop('actions'); + expect(actions?.length).toBe(1); + expect(actions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + onSelect: expect.any(Function), + disabled: expect.any(Function), + }), + ]) + ); + }); + test('should call onClickMultiValue with a correct data for multiple series selected', () => {}); + test('should call onClickMultiValue with a correct data for time selected', () => {}); + }); }); 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 e1ad4fa19d1c0..b8ac9d5cd0bbb 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 @@ -8,7 +8,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { css } from '@emotion/react'; -import { i18n } from '@kbn/i18n'; import { Chart, Settings, @@ -30,13 +29,12 @@ import { XYChartElementEvent, Tooltip, XYChartSeriesIdentifier, - TooltipValue, SettingsProps, } from '@elastic/charts'; import { partition } from 'lodash'; import { IconType } from '@elastic/eui'; import { PaletteRegistry } from '@kbn/coloring'; -import { Datatable, RenderMode } from '@kbn/expressions-plugin/common'; +import { RenderMode } from '@kbn/expressions-plugin/common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { EmptyPlaceholder, LegendToggle } from '@kbn/charts-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; @@ -113,6 +111,7 @@ import { XYCurrentTime } from './xy_current_time'; import './xy_chart.scss'; import { TooltipHeader } from './tooltip'; import { LegendColorPickerWrapperContext, LegendColorPickerWrapper } from './legend_color_picker'; +import { createSplitPoint, getTooltipActions, getXSeriesPoint } from './tooltip/tooltip_actions'; declare global { interface Window { @@ -150,7 +149,6 @@ export type XYChartRenderProps = Omit & { function nonNullable(v: T): v is NonNullable { return v != null; } - function getValueLabelsStyling(isHorizontal: boolean): { displayValue: RecursivePartial; } { @@ -184,28 +182,6 @@ function getIconForSeriesType(layer: CommonXYDataLayerConfig): IconType { ); } -function createSplitPoint( - accessor: string | number, - value: string | number, - rows: Datatable['rows'], - table: Datatable -) { - const splitPointRowIndex = rows.findIndex((row) => { - if (Array.isArray(value)) { - return value.includes(row[accessor]); - } - return row[accessor] === value; - }); - if (splitPointRowIndex !== -1) { - return { - row: splitPointRowIndex, - column: table.columns.findIndex((column) => column.id === accessor), - value: table.rows[splitPointRowIndex][accessor], - table, - }; - } -} - export const XYChartReportable = React.memo(XYChart); export function XYChart({ @@ -560,59 +536,11 @@ export function XYChart({ valueLabels !== ValueLabelModes.HIDE && getValueLabelsStyling(shouldRotate); - const filterSelectedTooltipValues = ( - tooltipSelectedValues: Array< - TooltipValue, XYChartSeriesIdentifier> - > - ) => { - const layerIndexes: number[] = []; - tooltipSelectedValues.forEach((v) => { - const index = dataLayers.findIndex((l) => - v.seriesIdentifier.seriesKeys.some((key: string | number) => - l.accessors.some( - (accessor) => getAccessorByDimension(accessor, l.table.columns) === key.toString() - ) - ) - ); - if (!layerIndexes.includes(index) && index !== -1) { - layerIndexes.push(index); - } - }); - - if (!layerIndexes.length) return; - layerIndexes.forEach((layerIndex) => { - const layer = dataLayers[layerIndex]; - const { table } = layer; - - if (layer.splitAccessors?.length !== 1) return; - - const splitAccessor = getAccessorByDimension(layer.splitAccessors[0], table.columns); - const filterValues = tooltipSelectedValues - .map((v) => v.datum?.[splitAccessor]) - .filter(nonNullable); - - const splitPoints = filterValues - .map((v) => - createSplitPoint(splitAccessor, v, formattedDatatables[layer.layerId].table.rows, table) - ) - .filter(nonNullable); - if (splitPoints.length) { - onClickMultiValue({ - data: { - column: splitPoints[0].column, - value: splitPoints.map(({ value }) => value), - table, - }, - }); - } - }); - }; - const clickHandler: ElementClickListener = ([elementEvent]) => { // this cast is safe because we are rendering a cartesian chart const [xyGeometry, xySeries] = elementEvent as XYChartElementEvent; - const layerIndex = dataLayers.findIndex((l) => + const layer = dataLayers.find((l) => xySeries.seriesKeys.some((key: string | number) => l.accessors.some( (accessor) => getAccessorByDimension(accessor, l.table.columns) === key.toString() @@ -620,42 +548,19 @@ export function XYChart({ ) ); - if (layerIndex === -1) { + if (!layer) { return; } - - const layer = dataLayers[layerIndex]; const { table } = layer; - const xColumn = layer.xAccessor && getColumnByAccessor(layer.xAccessor, table.columns); - const xAccessor = layer.xAccessor - ? getAccessorByDimension(layer.xAccessor, table.columns) - : undefined; - - const xFormat = xColumn ? fieldFormats[layer.layerId].xAccessors[xColumn.id] : undefined; - const currentXFormatter = - xAccessor && formattedDatatables[layer.layerId]?.formattedColumns[xAccessor] && xColumn - ? formatFactory(xFormat) - : xAxisFormatter; - - const rowIndex = table.rows.findIndex((row) => { - if (xAccessor) { - if (formattedDatatables[layer.layerId]?.formattedColumns[xAccessor]) { - // stringify the value to compare with the chart value - return currentXFormatter.convert(row[xAccessor]) === xyGeometry.x; - } - return row[xAccessor] === xyGeometry.x; - } - }); - - const points = [ - { - row: rowIndex, - column: table.columns.findIndex((col) => col.id === xAccessor), - value: xAccessor ? table.rows[rowIndex][xAccessor] : xyGeometry.x, - table, - }, - ]; + const xSeriesPoint = getXSeriesPoint( + layer, + xyGeometry.x, + fieldFormats, + formattedDatatables, + xAxisFormatter, + formatFactory + ); const splitPoints: FilterEvent['data']['data'] = []; @@ -700,7 +605,7 @@ export function XYChart({ } const context: FilterEvent['data'] = { - data: [...points, ...splitPoints], + data: [xSeriesPoint, ...splitPoints], }; onClickValue(context); }; @@ -796,8 +701,6 @@ export function XYChart({ overflowX: 'hidden', position: uiState ? 'absolute' : 'relative', }); - // enable the tooltip actions only if there is at least one splitAccessor to the dataLayer - const hasTooltipActions = dataLayers.some((dataLayer) => dataLayer.splitAccessors) && interactive; const { theme: settingsThemeOverrides = {}, ...settingsOverrides } = getOverridesFor( overrides, @@ -844,25 +747,15 @@ export function XYChart({ ) : undefined } - actions={ - !args.detailedTooltip && hasTooltipActions - ? [ - { - disabled: (selected) => selected.length < 1, - label: (selected) => - selected.length === 0 - ? i18n.translate('expressionXY.tooltipActions.emptyFilterSelection', { - defaultMessage: 'Select at least one series to filter', - }) - : i18n.translate('expressionXY.tooltipActions.filterValues', { - defaultMessage: 'Filter {seriesNumber} series', - values: { seriesNumber: selected.length }, - }), - onSelect: filterSelectedTooltipValues, - }, - ] - : undefined - } + actions={getTooltipActions( + dataLayers, + onClickMultiValue, + fieldFormats, + formattedDatatables, + xAxisFormatter, + formatFactory, + interactive && !args.detailedTooltip + )} customTooltip={ args.detailedTooltip ? ({ header, values }) => ( diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index 7f2af7005070d..901b7eb0568c6 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -32,6 +32,8 @@ "@kbn/kibana-react-plugin", "@kbn/chart-expressions-common", "@kbn/event-annotation-common", + "@kbn/visualization-ui-components", + "@kbn/es-query", ], "exclude": [ "target/**/*", diff --git a/src/plugins/content_management/public/mocks.ts b/src/plugins/content_management/public/mocks.ts index e868ac2d84453..c1fe460d4ce90 100644 --- a/src/plugins/content_management/public/mocks.ts +++ b/src/plugins/content_management/public/mocks.ts @@ -23,6 +23,7 @@ const createStartContract = (): ContentManagementPublicStart => { update: jest.fn(), delete: jest.fn(), search: jest.fn(), + mSearch: jest.fn(), } as unknown as ContentManagementPublicStart['client'], registry: { get: jest.fn(), diff --git a/src/plugins/dashboard/kibana.jsonc b/src/plugins/dashboard/kibana.jsonc index dbf2f49b4857d..c22d68173deb6 100644 --- a/src/plugins/dashboard/kibana.jsonc +++ b/src/plugins/dashboard/kibana.jsonc @@ -33,7 +33,8 @@ "savedObjectsTaggingOss", "screenshotMode", "usageCollection", - "taskManager" + "taskManager", + "serverless" ], "requiredBundles": ["kibanaReact", "kibanaUtils", "presentationUtil"] } diff --git a/src/plugins/dashboard/public/dashboard_actions/index.ts b/src/plugins/dashboard/public/dashboard_actions/index.ts index eb80313272826..8376ee1e65171 100644 --- a/src/plugins/dashboard/public/dashboard_actions/index.ts +++ b/src/plugins/dashboard/public/dashboard_actions/index.ts @@ -32,18 +32,15 @@ export const buildAllDashboardActions = async ({ plugins, allowByValueEmbeddables, }: BuildAllDashboardActionsProps) => { - const { uiSettings } = core; - const { uiActions, share, presentationUtil, savedObjectsManagement, savedObjectsTaggingOss } = - plugins; + const { uiActions, share, presentationUtil, savedObjectsTaggingOss, contentManagement } = plugins; const clonePanelAction = new ClonePanelAction(core.savedObjects); uiActions.registerAction(clonePanelAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction.id); const SavedObjectFinder = getSavedObjectFinder( - uiSettings, - core.http, - savedObjectsManagement, + contentManagement.client, + core.uiSettings, savedObjectsTaggingOss?.getTaggingApi() ); const changeViewAction = new ReplacePanelAction(SavedObjectFinder); diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx index d02a9a31ef667..697a516e1fc6d 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx +++ b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx @@ -37,6 +37,7 @@ export const DashboardListingPage = ({ }: DashboardListingPageProps) => { const { data: { query }, + serverless, chrome: { setBreadcrumbs }, dashboardContentManagement: { findDashboards }, } = pluginServices.getServices(); @@ -59,7 +60,13 @@ export const DashboardListingPage = ({ text: getDashboardBreadcrumb(), }, ]); - }, [setBreadcrumbs]); + + if (serverless?.setBreadcrumbs) { + // if serverless breadcrumbs available, + // reset any deeper context breadcrumbs to only keep the main "dashboard" part that comes from the navigation config + serverless.setBreadcrumbs([]); + } + }, [setBreadcrumbs, serverless]); useEffect(() => { // syncs `_g` portion of url with query services diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx index 5e80bdf3ec97a..d6e61f9e1c88b 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx @@ -61,6 +61,7 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr getIsVisible$: getChromeIsVisible$, recentlyAccessed: chromeRecentlyAccessed, }, + serverless, settings: { uiSettings }, navigation: { TopNavMenu }, embeddable: { getStateTransfer }, @@ -136,14 +137,7 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr * Set breadcrumbs to dashboard title when dashboard's title or view mode changes */ useEffect(() => { - setBreadcrumbs([ - { - text: getDashboardBreadcrumb(), - 'data-test-subj': 'dashboardListingBreadcrumb', - onClick: () => { - redirectTo({ destination: 'listing' }); - }, - }, + const dashboardTitleBreadcrumbs = [ { text: viewMode === ViewMode.EDIT ? ( @@ -160,8 +154,26 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr } : undefined, }, - ]); - }, [setBreadcrumbs, redirectTo, dashboardTitle, dashboard, viewMode]); + ]; + + if (serverless?.setBreadcrumbs) { + // set serverless breadcrumbs if available, + // set only the dashboardTitleBreadcrumbs because the main breadcrumbs automatically come as part of the navigation config + serverless.setBreadcrumbs(dashboardTitleBreadcrumbs); + } else { + // non-serverless regular breadcrumbs + setBreadcrumbs([ + { + text: getDashboardBreadcrumb(), + 'data-test-subj': 'dashboardListingBreadcrumb', + onClick: () => { + redirectTo({ destination: 'listing' }); + }, + }, + ...dashboardTitleBreadcrumbs, + ]); + } + }, [setBreadcrumbs, redirectTo, dashboardTitle, dashboard, viewMode, serverless]); /** * Build app leave handler whenever hasUnsavedChanges changes diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 41f10fd31d8bb..b42a13f858965 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -51,6 +51,7 @@ import type { import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import type { ServerlessPluginStart } from '@kbn/serverless/public'; import { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; @@ -107,6 +108,7 @@ export interface DashboardStartDependencies { usageCollection?: UsageCollectionStart; visualizations: VisualizationsStart; customBranding: CustomBrandingStart; + serverless?: ServerlessPluginStart; } export interface DashboardSetup { diff --git a/src/plugins/dashboard/public/services/content_management/content_management_service.stub.ts b/src/plugins/dashboard/public/services/content_management/content_management_service.stub.ts new file mode 100644 index 0000000000000..6f762fcadc40a --- /dev/null +++ b/src/plugins/dashboard/public/services/content_management/content_management_service.stub.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; + +export type ContentManagementServiceFactory = PluginServiceFactory; + +export const contentManagementServiceFactory: ContentManagementServiceFactory = () => { + return contentManagementMock.createStartContract(); +}; diff --git a/src/plugins/dashboard/public/services/content_management/content_management_service.ts b/src/plugins/dashboard/public/services/content_management/content_management_service.ts new file mode 100644 index 0000000000000..01c66a2c3682a --- /dev/null +++ b/src/plugins/dashboard/public/services/content_management/content_management_service.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import { DashboardStartDependencies } from '../../plugin'; + +export type ContentManagementServiceFactory = KibanaPluginServiceFactory< + ContentManagementPublicStart, + DashboardStartDependencies +>; + +export const contentManagementServiceFactory: ContentManagementServiceFactory = ({ + startPlugins, +}) => { + const { contentManagement } = startPlugins; + + return contentManagement; +}; diff --git a/src/plugins/dashboard/public/services/plugin_services.stub.ts b/src/plugins/dashboard/public/services/plugin_services.stub.ts index e54cb783c71d6..0ae4159ed2128 100644 --- a/src/plugins/dashboard/public/services/plugin_services.stub.ts +++ b/src/plugins/dashboard/public/services/plugin_services.stub.ts @@ -40,6 +40,8 @@ import { visualizationsServiceFactory } from './visualizations/visualizations.st import { dashboardContentManagementServiceFactory } from './dashboard_content_management/dashboard_content_management.stub'; import { customBrandingServiceFactory } from './custom_branding/custom_branding.stub'; import { savedObjectsManagementServiceFactory } from './saved_objects_management/saved_objects_management_service.stub'; +import { contentManagementServiceFactory } from './content_management/content_management_service.stub'; +import { serverlessServiceFactory } from './serverless/serverless_service.stub'; export const providers: PluginServiceProviders = { dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory), @@ -68,6 +70,8 @@ export const providers: PluginServiceProviders = { visualizations: new PluginServiceProvider(visualizationsServiceFactory), customBranding: new PluginServiceProvider(customBrandingServiceFactory), savedObjectsManagement: new PluginServiceProvider(savedObjectsManagementServiceFactory), + contentManagement: new PluginServiceProvider(contentManagementServiceFactory), + serverless: new PluginServiceProvider(serverlessServiceFactory), }; export const registry = new PluginServiceRegistry(providers); diff --git a/src/plugins/dashboard/public/services/plugin_services.ts b/src/plugins/dashboard/public/services/plugin_services.ts index 716010cdbf0a8..d84b55d0ff4a1 100644 --- a/src/plugins/dashboard/public/services/plugin_services.ts +++ b/src/plugins/dashboard/public/services/plugin_services.ts @@ -41,6 +41,8 @@ import { analyticsServiceFactory } from './analytics/analytics_service'; import { customBrandingServiceFactory } from './custom_branding/custom_branding_service'; import { savedObjectsManagementServiceFactory } from './saved_objects_management/saved_objects_management_service'; import { dashboardContentManagementServiceFactory } from './dashboard_content_management/dashboard_content_management_service'; +import { contentManagementServiceFactory } from './content_management/content_management_service'; +import { serverlessServiceFactory } from './serverless/serverless_service'; const providers: PluginServiceProviders = { dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory, [ @@ -82,6 +84,8 @@ const providers: PluginServiceProviders(); diff --git a/src/plugins/dashboard/public/services/serverless/serverless_service.stub.ts b/src/plugins/dashboard/public/services/serverless/serverless_service.stub.ts new file mode 100644 index 0000000000000..d9af53f1ddc0d --- /dev/null +++ b/src/plugins/dashboard/public/services/serverless/serverless_service.stub.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { DashboardServerlessService } from './types'; + +export type ServerlessServiceFactory = PluginServiceFactory; + +export const serverlessServiceFactory: ServerlessServiceFactory = () => { + return {}; +}; diff --git a/src/plugins/dashboard/public/services/serverless/serverless_service.ts b/src/plugins/dashboard/public/services/serverless/serverless_service.ts new file mode 100644 index 0000000000000..c22eea4560183 --- /dev/null +++ b/src/plugins/dashboard/public/services/serverless/serverless_service.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { DashboardStartDependencies } from '../../plugin'; +import { DashboardServerlessService } from './types'; + +export type ServerlessServiceFactory = KibanaPluginServiceFactory< + DashboardServerlessService, + DashboardStartDependencies +>; + +export const serverlessServiceFactory: ServerlessServiceFactory = ({ startPlugins }) => { + const { serverless } = startPlugins; + + return { setBreadcrumbs: serverless?.setBreadcrumbs }; +}; diff --git a/src/plugins/saved_objects_finder/server/routes/index.ts b/src/plugins/dashboard/public/services/serverless/types.ts similarity index 65% rename from src/plugins/saved_objects_finder/server/routes/index.ts rename to src/plugins/dashboard/public/services/serverless/types.ts index df9a797e8184d..ce12102fa87c3 100644 --- a/src/plugins/saved_objects_finder/server/routes/index.ts +++ b/src/plugins/dashboard/public/services/serverless/types.ts @@ -6,9 +6,8 @@ * Side Public License, v 1. */ -import { SavedObjectsRouter } from '../types'; -import { registerFindRoute } from './find'; +import type { ServerlessPluginStart } from '@kbn/serverless/public'; -export const registerRoutes = (router: SavedObjectsRouter) => { - registerFindRoute(router); -}; +export interface DashboardServerlessService { + setBreadcrumbs?: ServerlessPluginStart['setBreadcrumbs']; +} diff --git a/src/plugins/dashboard/public/services/types.ts b/src/plugins/dashboard/public/services/types.ts index 2bb3753b132f0..13adaf6098070 100644 --- a/src/plugins/dashboard/public/services/types.ts +++ b/src/plugins/dashboard/public/services/types.ts @@ -9,6 +9,7 @@ import { PluginInitializerContext } from '@kbn/core/public'; import { KibanaPluginServiceParams } from '@kbn/presentation-util-plugin/public'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { DashboardStartDependencies } from '../plugin'; import { DashboardAnalyticsService } from './analytics/types'; @@ -36,6 +37,7 @@ import { DashboardSpacesService } from './spaces/types'; import { DashboardUrlForwardingService } from './url_forwarding/types'; import { DashboardUsageCollectionService } from './usage_collection/types'; import { DashboardVisualizationsService } from './visualizations/types'; +import { DashboardServerlessService } from './serverless/types'; export type DashboardPluginServiceParams = KibanaPluginServiceParams & { initContext: PluginInitializerContext; // need a custom type so that initContext is a required parameter for initializerContext @@ -68,4 +70,6 @@ export interface DashboardServices { visualizations: DashboardVisualizationsService; customBranding: DashboardCustomBrandingService; savedObjectsManagement: SavedObjectsManagementPluginStart; + contentManagement: ContentManagementPublicStart; + serverless: DashboardServerlessService; // TODO: make this optional in follow up } diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 6fd24d4e1fb01..9d5dafa47c88a 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -63,7 +63,8 @@ "@kbn/content-management-table-list-view", "@kbn/content-management-table-list-view-table", "@kbn/shared-ux-prompt-not-found", - "@kbn/content-management-content-editor" + "@kbn/content-management-content-editor", + "@kbn/serverless" ], "exclude": ["target/**/*"] } diff --git a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts index e39c858f3e903..981ff3664fbec 100644 --- a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts @@ -10,7 +10,7 @@ import { buildOtherBucketAgg, mergeOtherBucketAggResponse, updateMissingBucket, - OTHER_BUCKET_SEPARATOR as SEP, + OTHER_NESTED_BUCKET_SEPARATOR as SEP, constructSingleTermOtherFilter, } from './_terms_other_bucket_helper'; import type { DataViewField, DataView } from '@kbn/data-views-plugin/common'; @@ -97,188 +97,121 @@ const nestedTerm = { ], }; -const singleTermResponse = { - took: 10, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, +function wrapResponse(aggregationResponse: Record) { + return { + took: 3, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: 14005, max_score: 0, hits: [] }, + aggregations: aggregationResponse, + status: 200, + }; +} + +const singleTermResponse = wrapResponse({ + '1': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, + buckets: [ + { key: 'ios', doc_count: 2850 }, + { key: 'win xp', doc_count: 2830 }, + { key: '__missing__', doc_count: 1430 }, + ], }, - hits: { - total: 14005, - max_score: 0, - hits: [], - }, - aggregations: { - '1': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 8325, - buckets: [ - { key: 'ios', doc_count: 2850 }, - { key: 'win xp', doc_count: 2830 }, - { key: '__missing__', doc_count: 1430 }, - ], - }, - }, - status: 200, -}; +}); -const nestedTermResponse = { - took: 10, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: 14005, - max_score: 0, - hits: [], - }, - aggregations: { - '1': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 8325, - buckets: [ - { - '2': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 8325, - buckets: [ - { key: 'ios', doc_count: 2850 }, - { key: 'win xp', doc_count: 2830 }, - { key: '__missing__', doc_count: 1430 }, - ], - }, - key: 'US-with-dash', - doc_count: 2850, +const nestedTermResponse = wrapResponse({ + '1': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, + buckets: [ + { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, + buckets: [ + { key: 'ios', doc_count: 2850 }, + { key: 'win xp', doc_count: 2830 }, + { key: '__missing__', doc_count: 1430 }, + ], }, - { - '2': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 8325, - buckets: [ - { key: 'ios', doc_count: 1850 }, - { key: 'win xp', doc_count: 1830 }, - { key: '__missing__', doc_count: 130 }, - ], - }, - key: 'IN-with-dash', - doc_count: 2830, + key: 'US-with-dash', + doc_count: 2850, + }, + { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, + buckets: [ + { key: 'ios', doc_count: 1850 }, + { key: 'win xp', doc_count: 1830 }, + { key: '__missing__', doc_count: 130 }, + ], }, - ], - }, + key: 'IN-with-dash', + doc_count: 2830, + }, + ], }, - status: 200, -}; +}); -const exhaustiveNestedTermResponse = { - took: 10, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: 14005, - max_score: 0, - hits: [], - }, - aggregations: { - '1': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 8325, - buckets: [ - { - '2': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'ios', doc_count: 2850 }, - { key: 'win xp', doc_count: 2830 }, - { key: '__missing__', doc_count: 1430 }, - ], - }, - key: 'US-with-dash', - doc_count: 2850, +const exhaustiveNestedTermResponse = wrapResponse({ + '1': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, + buckets: [ + { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'ios', doc_count: 2850 }, + { key: 'win xp', doc_count: 2830 }, + { key: '__missing__', doc_count: 1430 }, + ], }, - { - '2': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'ios', doc_count: 1850 }, - { key: 'win xp', doc_count: 1830 }, - { key: '__missing__', doc_count: 130 }, - ], - }, - key: 'IN-with-dash', - doc_count: 2830, + key: 'US-with-dash', + doc_count: 2850, + }, + { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'ios', doc_count: 1850 }, + { key: 'win xp', doc_count: 1830 }, + { key: '__missing__', doc_count: 130 }, + ], }, - ], - }, + key: 'IN-with-dash', + doc_count: 2830, + }, + ], }, - status: 200, -}; +}); -const nestedTermResponseNoResults = { - took: 10, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: 0, - max_score: null, - hits: [], - }, - aggregations: { - '1': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, +const nestedTermResponseNoResults = wrapResponse({ + '1': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], }, - status: 200, -}; +}); -const singleOtherResponse = { - took: 3, - timed_out: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { total: 14005, max_score: 0, hits: [] }, - aggregations: { - 'other-filter': { - buckets: { '': { doc_count: 2805 } }, - }, +const singleOtherResponse = wrapResponse({ + 'other-filter': { + buckets: { '': { doc_count: 2805 } }, }, - status: 200, -}; +}); -const nestedOtherResponse = { - took: 3, - timed_out: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { total: 14005, max_score: 0, hits: [] }, - aggregations: { - 'other-filter': { - buckets: { - [`${SEP}US-with-dash`]: { doc_count: 2805 }, - [`${SEP}IN-with-dash`]: { doc_count: 2804 }, - }, +const nestedOtherResponse = wrapResponse({ + 'other-filter': { + buckets: { + [`${SEP}US-with-dash`]: { doc_count: 2805 }, + [`${SEP}IN-with-dash`]: { doc_count: 2804 }, }, }, - status: 200, -}; +}); describe('Terms Agg Other bucket helper', () => { const typesRegistry = mockAggTypesRegistry(); @@ -326,28 +259,12 @@ describe('Terms Agg Other bucket helper', () => { }); test('returns a function for undefined agg buckets', () => { - const response = { - took: 10, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: 14005, - max_score: 0, - hits: [], - }, - aggregations: { - 2: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 8325, - }, + const response = wrapResponse({ + 2: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, }, - status: 200, - }; + }); const aggConfigs = getAggConfigs(nestedTerm.aggs); const agg = buildOtherBucketAgg( aggConfigs, @@ -706,22 +623,15 @@ describe('Terms Agg Other bucket helper', () => { }, }; - const otherResponse = { - took: 3, - timed_out: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { total: 14005, max_score: 0, hits: [] }, - aggregations: { - 'other-filter': { - buckets: { - [`${SEP}US-with-dash`]: { doc_count: 2805 }, - [`${SEP}IN-with-dash`]: { doc_count: 2804 }, - [`${SEP}`]: { doc_count: 2804 }, - }, + const otherResponse = wrapResponse({ + 'other-filter': { + buckets: { + [`${SEP}US-with-dash`]: { doc_count: 2805 }, + [`${SEP}IN-with-dash`]: { doc_count: 2804 }, + [`${SEP}`]: { doc_count: 2804 }, }, }, - status: 200, - }; + }); const aggConfigs = getAggConfigs(nestedTerm.aggs); const otherAggConfig = buildOtherBucketAgg( aggConfigs, @@ -745,6 +655,269 @@ describe('Terms Agg Other bucket helper', () => { expect((topAgg['1'] as any).buckets[2]['2'].buckets[3].key).toEqual('__other__'); } }); + + test('correctly merges other bucket when the term response contains an empty string', () => { + const response = wrapResponse({ + '0': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 82, + buckets: [ + { + key: '', + doc_count: 657, + }, + { + key: 'ios', + doc_count: 300, + }, + { + key: 'win xp', + doc_count: 284, + }, + { + doc_count: 82, + filters: [ + { + meta: { + type: 'phrases', + key: 'machine.os.raw', + params: ['ios', 'win xp', ''], + negate: true, + }, + query: { + bool: { + should: [ + { + match_phrase: { + 'machine.os.raw': '', + }, + }, + { + match_phrase: { + 'machine.os.raw': 'ios', + }, + }, + { + match_phrase: { + 'machine.os.raw': 'win xp', + }, + }, + ], + minimum_should_match: 1, + }, + }, + }, + ], + key: '__other__', + }, + ], + }, + }); + + const otherResponse = wrapResponse({ + 'other-filter': { + buckets: { + '': { + doc_count: 82, + filters: [ + { + meta: { + type: 'phrases', + key: 'machine.os.raw', + params: ['ios', 'win xp', ''], + negate: true, + }, + query: { + bool: { + should: [ + { + match_phrase: { + 'machine.os.raw': 'ios', + }, + }, + { + match_phrase: { + 'machine.os.raw': 'win xp', + }, + }, + { + match_phrase: { + 'machine.os.raw': '', + }, + }, + ], + minimum_should_match: 1, + }, + }, + }, + ], + key: '__other__', + }, + }, + }, + }); + const aggConfigs = getAggConfigs(singleTerm.aggs); + const otherAggConfig = buildOtherBucketAgg( + aggConfigs, + aggConfigs.aggs[0] as IBucketAggConfig, + enrichResponseWithSampling(response) + ); + + expect(otherAggConfig).toBeDefined(); + if (otherAggConfig) { + const mergedResponse = mergeOtherBucketAggResponse( + aggConfigs, + enrichResponseWithSampling(response), + enrichResponseWithSampling(otherResponse), + aggConfigs.aggs[0] as IBucketAggConfig, + otherAggConfig(), + constructSingleTermOtherFilter + ); + + const topAgg = getTopAggregations(mergedResponse); + const topBuckets = (topAgg['1'] as any).buckets; + expect(topBuckets[2].key).toEqual(''); + expect(topBuckets[2]['2'].buckets[3].key).toEqual('__other__'); + } + }); + + test('correctly merges other bucket with both top and nested terms agg have empty string', () => { + const response = wrapResponse({ + ...nestedTermResponse.aggregations, + '1': { + ...nestedTermResponse.aggregations['1'], + buckets: [ + ...nestedTermResponse.aggregations['1'].buckets, + { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, + buckets: [ + { key: 'ios', doc_count: 1850 }, + { key: 'win xp', doc_count: 1830 }, + { key: '__missing__', doc_count: 130 }, + { key: '', doc_count: 130 }, + ], + }, + key: '', + doc_count: 2830, + }, + ], + }, + }); + + const otherRootResponse = wrapResponse({ + 'other-filter': { + buckets: { + '': { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 59, + buckets: [ + { + key: 'US-with-dash', + doc_count: 13, + }, + { + key: 'IN-with-dash', + doc_count: 12, + }, + { + key: '', + doc_count: 12, + }, + ], + }, + doc_count: 84, + key: '__other__', + filters: [ + { + meta: { + type: 'phrases', + key: 'machine.os.raw', + params: ['', 'ios', 'win xp'], + negate: true, + }, + query: { + bool: { + should: [ + { + match_phrase: { + 'machine.os.raw': '', + }, + }, + { + match_phrase: { + 'machine.os.raw': 'ios', + }, + }, + { + match_phrase: { + 'machine.os.raw': 'win xp', + }, + }, + ], + minimum_should_match: 1, + }, + }, + }, + ], + }, + }, + }, + }); + + const otherResponse = wrapResponse({ + 'other-filter': { + buckets: { + [`${SEP}US-with-dash`]: { doc_count: 2805 }, + [`${SEP}IN-with-dash`]: { doc_count: 2804 }, + [`${SEP}`]: { doc_count: 2804 }, + [`${SEP}__other__`]: { doc_count: 3000 }, + }, + }, + }); + const aggConfigs = getAggConfigs(nestedTerm.aggs); + const otherRootAggConfig = buildOtherBucketAgg( + aggConfigs, + aggConfigs.aggs[0] as IBucketAggConfig, + enrichResponseWithSampling(response) + ); + + if (otherRootAggConfig) { + const mergedTopResponse = mergeOtherBucketAggResponse( + aggConfigs, + enrichResponseWithSampling(response), + enrichResponseWithSampling(otherRootResponse), + aggConfigs.aggs[0] as IBucketAggConfig, + otherRootAggConfig(), + constructSingleTermOtherFilter + ); + + const otherAggConfig = buildOtherBucketAgg( + aggConfigs, + aggConfigs.aggs[1] as IBucketAggConfig, + enrichResponseWithSampling(mergedTopResponse) + ); + if (otherAggConfig) { + const mergedResponse = mergeOtherBucketAggResponse( + aggConfigs, + enrichResponseWithSampling(mergedTopResponse), + enrichResponseWithSampling(otherResponse), + aggConfigs.aggs[1] as IBucketAggConfig, + otherAggConfig(), + constructSingleTermOtherFilter + ); + + const topAgg = getTopAggregations(mergedResponse); + expect((topAgg['1'] as any).buckets[2].key).toEqual(''); + expect((topAgg['1'] as any).buckets[2]['2'].buckets[4].key).toEqual('__other__'); + + expect((topAgg['1'] as any).buckets[3].key).toEqual('__other__'); + expect((topAgg['1'] as any).buckets[3]['2'].buckets[3].key).toEqual('__other__'); + } + } + }); }); describe(`updateMissingBucket${getTitlePostfix()}`, () => { diff --git a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts index d4542d1513f16..9737883733266 100644 --- a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts +++ b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts @@ -22,7 +22,9 @@ import { IAggType } from '../agg_type'; import { IAggConfig } from '../agg_config'; import { createSamplerAgg } from '../utils/sampler'; -export const OTHER_BUCKET_SEPARATOR = '╰┄►'; +const MISSING_KEY_STRING = '__missing__'; +export const OTHER_NESTED_BUCKET_SEPARATOR = '╰┄►'; +const otherBucketRegexp = new RegExp(`^${OTHER_NESTED_BUCKET_SEPARATOR}`); /** * walks the aggregation DSL and returns DSL starting at aggregation with id of startFromAggId @@ -56,10 +58,11 @@ const getAggResultBuckets = ( aggWithOtherBucket: IAggConfig, key: string ) => { - const keyParts = key.split(OTHER_BUCKET_SEPARATOR); + const keyParts = key.split(OTHER_NESTED_BUCKET_SEPARATOR); let responseAgg = response; for (const i in keyParts) { - if (keyParts[i] || keyParts[i] === '') { + // enable also the empty string + if (keyParts[i] != null) { const responseAggs: Array> = values(responseAgg); // If you have multi aggs, we cannot just assume the first one is the `other` bucket, // so we need to loop over each agg until we find it. @@ -75,8 +78,13 @@ const getAggResultBuckets = ( return bucketKey === keyParts[i]; }); if (aggResultBucket) { - responseAgg = aggResultBucket; - break; + // this is a special check in order to avoid an overwrite when + // there's an empty string term at root level for the data request + // as the other request will default to empty string category as well + if (!responseAgg?.[aggWithOtherBucket.id] || keyParts[i] !== '') { + responseAgg = aggResultBucket; + break; + } } } } @@ -94,19 +102,20 @@ const getAggResultBuckets = ( * @param aggId: id of the aggregation with missing bucket */ const getAggConfigResultMissingBuckets = (responseAggs: any, aggId: string) => { - const missingKey = '__missing__'; - let resultBuckets: Array> = []; + const resultBuckets: Array> = []; if (responseAggs[aggId]) { const matchingBucket = responseAggs[aggId].buckets.find( - (bucket: Record) => bucket.key === missingKey + (bucket: Record) => bucket.key === MISSING_KEY_STRING ); - if (matchingBucket) resultBuckets.push(matchingBucket); + if (matchingBucket) { + resultBuckets.push(matchingBucket); + } return resultBuckets; } each(responseAggs, (agg) => { if (agg.buckets) { each(agg.buckets, (bucket) => { - resultBuckets = [...resultBuckets, ...getAggConfigResultMissingBuckets(bucket, aggId)]; + resultBuckets.push(...getAggConfigResultMissingBuckets(bucket, aggId)); }); } }); @@ -124,7 +133,7 @@ const getOtherAggTerms = (requestAgg: Record, key: string, otherAgg return requestAgg['other-filter'].filters.filters[key].bool.must_not .filter( (filter: Record) => - filter.match_phrase && filter.match_phrase[otherAgg.params.field.name] + filter.match_phrase && filter.match_phrase[otherAgg.params.field.name] != null // mind empty strings! ) .map((filter: Record) => filter.match_phrase[otherAgg.params.field.name]); }; @@ -220,7 +229,7 @@ export const buildOtherBucketAgg = ( bucket, newAgg.id, newFilters, - `${key}${OTHER_BUCKET_SEPARATOR}${bucketKey.toString()}` + `${key}${OTHER_NESTED_BUCKET_SEPARATOR}${bucketKey.toString()}` ); }); return; @@ -229,7 +238,7 @@ export const buildOtherBucketAgg = ( const hasScriptedField = !!aggWithOtherBucket.params.field?.scripted; const hasMissingBucket = !!aggWithOtherBucket.params.missingBucket; const hasMissingBucketKey = agg.buckets.some( - (bucket: { key: string }) => bucket.key === '__missing__' + (bucket: { key: string }) => bucket.key === MISSING_KEY_STRING ); if ( aggWithOtherBucket.params.field && @@ -246,7 +255,7 @@ export const buildOtherBucketAgg = ( // create not filters for all the buckets each(agg.buckets, (bucket) => { - if (bucket.key === '__missing__') return; + if (bucket.key === MISSING_KEY_STRING) return; const filter = currentAgg.createFilter(currentAgg.getKey(bucket, bucket.key)); filter.meta.negate = true; filters.push(filter); @@ -302,7 +311,7 @@ export const mergeOtherBucketAggResponse = ( 'buckets' in aggregationsRoot!['other-filter'] ? aggregationsRoot!['other-filter'].buckets : {}; each(buckets, (bucket, key) => { if (!bucket.doc_count || key === undefined) return; - const bucketKey = key.replace(new RegExp(`^${OTHER_BUCKET_SEPARATOR}`), ''); + const bucketKey = key.replace(otherBucketRegexp, ''); const aggResultBuckets = getAggResultBuckets( aggsConfig, updatedAggregationsRoot, @@ -319,7 +328,7 @@ export const mergeOtherBucketAggResponse = ( if ( aggResultBuckets.some( - (aggResultBucket: Record) => aggResultBucket.key === '__missing__' + (aggResultBucket: Record) => aggResultBucket.key === MISSING_KEY_STRING ) ) { bucket.filters.push( @@ -330,7 +339,6 @@ export const mergeOtherBucketAggResponse = ( }); return updatedResponse; }; - export const updateMissingBucket = ( response: estypes.SearchResponse, aggConfigs: IAggConfigs, @@ -342,7 +350,7 @@ export const updateMissingBucket = ( agg.id ); aggResultBuckets.forEach((bucket) => { - bucket.key = '__missing__'; + bucket.key = MISSING_KEY_STRING; }); return updatedResponse; }; @@ -394,7 +402,7 @@ export const createOtherBucketPostFlightRequest = ( nestedSearchSource.setField('aggs', filterAgg); - const { rawResponse: response } = await lastValueFrom( + const { rawResponse: otherResponse } = await lastValueFrom( nestedSearchSource.fetch$({ abortSignal, sessionId: searchSessionId, @@ -416,7 +424,7 @@ export const createOtherBucketPostFlightRequest = ( resp = mergeOtherBucketAggResponse( aggConfigs, resp, - response, + otherResponse, aggConfig, filterAgg(), otherFilterBuilder diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 0ba2c77c8c016..a9d48108d296d 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -543,6 +543,8 @@ export class SearchSource { return search({ params, indexType: searchRequest.indexType }, options).pipe( switchMap((response) => { + // For testing timeout messages in UI, uncomment the next line + // response.rawResponse.timed_out = true; return new Observable>((obs) => { if (isErrorResponse(response)) { obs.error(response); @@ -916,6 +918,20 @@ export class SearchSource { }; body.query = buildEsQuery(index, query, filters, esQueryConfigs); + // For testing shard failure messages in UI, uncomment the next block and switch to `kibana*` data view + // body.query = { + // error_query: { + // indices: [ + // { + // name: 'kibana_sample_data_logs', + // shard_ids: [0, 1], + // error_type: 'exception', + // message: 'Testing shard failures!', + // }, + // ], + // }, + // }; + if (highlightAll && body.query) { body.highlight = getHighlightRequest(getConfig(UI_SETTINGS.DOC_HIGHLIGHT)); delete searchRequest.highlightAll; diff --git a/src/plugins/data/kibana.jsonc b/src/plugins/data/kibana.jsonc index 90658599a87bf..2881da532d63a 100644 --- a/src/plugins/data/kibana.jsonc +++ b/src/plugins/data/kibana.jsonc @@ -28,7 +28,6 @@ ], "optionalPlugins": [ "usageCollection", - "taskManager", "security" ], "requiredBundles": [ diff --git a/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.test.ts index 804f898945e57..f138544065b82 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.test.ts @@ -11,50 +11,81 @@ import { dataPluginMock } from '../../mocks'; import { setIndexPatterns, setSearchService } from '../../services'; import { createFiltersFromMultiValueClickAction } from './create_filters_from_multi_value_click'; import { FieldFormatsGetConfigFn, BytesFormat } from '@kbn/field-formats-plugin/common'; +import { Datatable } from '@kbn/expressions-plugin/common'; +import { BooleanRelation, Filter } from '@kbn/es-query'; const mockField = { name: 'bytes', filterable: true, }; +let table: Pick; + describe('createFiltersFromMultiValueClickAction', () => { let dataPoints: Parameters[0]['data']; beforeEach(() => { - dataPoints = { - table: { - columns: [ - { - name: 'test', - id: '1-1', - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - indexPatternId: 'logstash-*', - type: 'histogram', - params: { - field: 'bytes', - interval: 30, - otherBucket: true, - }, + table = { + columns: [ + { + name: 'test', + id: '1-1', + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + indexPatternId: 'logstash-*', + type: 'histogram', + params: { + field: 'bytes', + interval: 30, + otherBucket: true, }, }, }, - ], - rows: [ - { - '1-1': '2048', + }, + { + name: 'avg', + id: '2-2', + meta: { + type: 'number', + source: 'esaggs', + sourceParams: { + indexPatternId: 'logstash-*', + type: 'average', + params: { + field: 'bytes', + }, + }, }, - ], - meta: { - source: 'dataview-1', - type: 'esaggs', }, + ], + rows: [ + { + '1-1': 1691189380, + '2-2': 2048, + }, + { + '1-1': 1691189680, + '2-2': 90, + }, + ], + meta: { + source: 'dataview-1', + type: 'esaggs', }, - column: 0, - value: ['2048'], }; + dataPoints = [ + { + table, + cells: [ + { + column: 0, + row: 0, + }, + ], + }, + ]; const dataStart = dataPluginMock.createStartContract(); setSearchService(dataStart.search); @@ -72,30 +103,117 @@ describe('createFiltersFromMultiValueClickAction', () => { }); test('ignores event when value for rows is not provided', async () => { - dataPoints.table.rows[0]['1-1'] = null; + dataPoints[0].table.rows[0]['1-1'] = null; const filters = await createFiltersFromMultiValueClickAction({ data: dataPoints }); expect(filters).toBeUndefined(); }); test('ignores event when dataview id is not provided', async () => { - dataPoints.table.meta = undefined; + dataPoints[0].table.meta = undefined; const filters = await createFiltersFromMultiValueClickAction({ data: dataPoints }); expect(filters).toBeUndefined(); }); test('handles an event when aggregations type is a terms', async () => { - (dataPoints.table.columns[0].meta.sourceParams as any).type = 'terms'; - const filters = await createFiltersFromMultiValueClickAction({ data: dataPoints }); + (dataPoints[0].table.columns[0].meta.sourceParams as any).type = 'terms'; + const filters = (await createFiltersFromMultiValueClickAction({ + data: dataPoints, + })) as Filter[]; - expect(filters?.query?.match_phrase?.bytes).toEqual('2048'); + expect(filters[0]?.query?.match_phrase?.bytes).toEqual(1691189380); }); test('handles an event when aggregations type is not terms', async () => { - const filters = await createFiltersFromMultiValueClickAction({ data: dataPoints }); + const filters = (await createFiltersFromMultiValueClickAction({ + data: dataPoints, + })) as Filter[]; - expect(filters?.query?.range.bytes.gte).toEqual(2048); - expect(filters?.query?.range.bytes.lt).toEqual(2078); + expect(filters[0]?.query?.range.bytes.gte).toEqual(1691189380); + expect(filters[0]?.query?.range.bytes.lt).toEqual(1691189410); + }); + test('creates combined filters if relation is passed', async () => { + dataPoints[0].cells = [ + { + column: 0, + row: 0, + }, + { + column: 0, + row: 1, + }, + ]; + dataPoints[0].relation = BooleanRelation.OR; + const filters = (await createFiltersFromMultiValueClickAction({ + data: dataPoints, + })) as Filter[]; + expect(filters.length).toEqual(1); + expect(filters[0]?.meta.type).toEqual('combined'); + }); + test('creates separate filters if relation is not passed', async () => { + dataPoints[0].cells = [ + { + column: 0, + row: 0, + }, + { + column: 0, + row: 1, + }, + ]; + const filters = (await createFiltersFromMultiValueClickAction({ + data: dataPoints, + })) as Filter[]; + expect(filters.length).toEqual(2); + }); + test('creates separate filters for multiple tables', async () => { + dataPoints = [ + { + table, + cells: [ + { + column: 0, + row: 0, + }, + ], + }, + { + table, + cells: [ + { + column: 0, + row: 1, + }, + ], + }, + ]; + const filters = (await createFiltersFromMultiValueClickAction({ + data: dataPoints, + })) as Filter[]; + expect(filters.length).toEqual(2); + }); + test('doesnt combine duplicate filters', async () => { + dataPoints = [ + { + table, + cells: [ + { + column: 0, + row: 0, + }, + { + column: 0, + row: 0, + }, + ], + relation: BooleanRelation.OR, + }, + ]; + const filters = (await createFiltersFromMultiValueClickAction({ + data: dataPoints, + })) as Filter[]; + expect(filters.length).toEqual(1); + expect(filters[0]?.meta?.type).toEqual('range'); }); }); diff --git a/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.ts b/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.ts index b33f994015cfe..5bce14cedf1cd 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_multi_value_click.ts @@ -5,68 +5,77 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { buildCombinedFilter, Filter, toggleFilterNegated, BooleanRelation } from '@kbn/es-query'; +import { + buildCombinedFilter, + toggleFilterNegated, + Filter, + compareFilters, + COMPARE_ALL_OPTIONS, +} from '@kbn/es-query'; +import { Truthy, uniqWith } from 'lodash'; import { createFilter } from './create_filters_from_value_click'; import type { MultiValueClickContext } from '../multi_value_click_action'; +import { mapAndFlattenFilters } from '../../query'; type MultiValueClickDataContext = MultiValueClickContext['data']; +export const truthy = (value: T): value is Truthy => !!value; + /** @public */ export const createFiltersFromMultiValueClickAction = async ({ data, negate, -}: MultiValueClickDataContext) => { - const { table, column, value } = data; - const dataViewId = table?.meta?.source; - if (!dataViewId) return; - - const columnId = table.columns[column].id; +}: MultiValueClickDataContext): Promise => { + if (!data || data.length === 0) return; - const filters = ( + const result = ( await Promise.all( - value.map(async (v) => { - return ( - await createFilter( - table, - column, - table.rows.findIndex((r) => r[columnId] === v) + data.map(async (d) => { + const { table, cells, relation } = d; + const dataViewId = table?.meta?.source; + if (!dataViewId) return; + + const filters = ( + await Promise.all( + cells.map(async ({ column, row }) => await createFilter(table, column, row)) ) - )?.[0]; + ) + .flat() + .filter(truthy); + + const uniqueFilters = uniqWith(mapAndFlattenFilters(filters), (a, b) => + compareFilters(a, b, COMPARE_ALL_OPTIONS) + ); + + if (uniqueFilters.length === 0) return; + + if (uniqueFilters.length === 1) { + return negate ? [toggleFilterNegated(uniqueFilters[0])] : uniqueFilters; + } + + if (!relation) { + return negate ? uniqueFilters.map((f) => toggleFilterNegated(f)) : uniqueFilters; + } + + const filtersHaveAlias = uniqueFilters.every((f) => f.meta.alias); + const alias = filtersHaveAlias + ? uniqueFilters.map((f) => f.meta.alias).join(` ${relation} `) + : ''; + + return buildCombinedFilter( + relation, + uniqueFilters, + { id: dataViewId }, + undefined, + negate, + alias + ); }) ) - ).filter(Boolean) as Filter[]; - if (filters.length === 0) return; - // no need for combined filter in case of one filter - if (filters.length === 1) { - if (filters[0] && negate) { - return toggleFilterNegated(filters[0]); - } - return filters[0]; - } - const filtersHaveAlias = filters.every((f) => f.meta.alias); - let alias = ''; - if (filtersHaveAlias) { - filters.forEach((f, i) => { - if (i === filters.length - 1) { - alias += `${f.meta.alias}`; - } else { - alias += `${f.meta.alias} ${BooleanRelation.OR} `; - } - }); - } - let filter: Filter = buildCombinedFilter( - BooleanRelation.OR, - filters, - { - id: dataViewId, - }, - undefined, - undefined, - alias - ); - if (filter && negate) { - filter = toggleFilterNegated(filter); - } + ) + .flat() + .filter(truthy); - return filter; + if (result.length === 0) return; + return result; }; diff --git a/src/plugins/data/public/actions/multi_value_click_action.ts b/src/plugins/data/public/actions/multi_value_click_action.ts index 422b1d8653e02..1f9059ae5e866 100644 --- a/src/plugins/data/public/actions/multi_value_click_action.ts +++ b/src/plugins/data/public/actions/multi_value_click_action.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import type { Filter } from '@kbn/es-query'; import { Datatable } from '@kbn/expressions-plugin/public'; import { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; -import { FilterManager } from '../query'; +import { BooleanRelation, extractTimeFilter, convertRangeFilterToTimeRange } from '@kbn/es-query'; +import { QueryStart } from '../query'; import { createFiltersFromMultiValueClickAction } from './filters/create_filters_from_multi_value_click'; export type MultiValueClickActionContext = MultiValueClickContext; @@ -20,18 +20,21 @@ export interface MultiValueClickContext { // Apps using this property will need to cast to `IEmbeddable`. embeddable?: unknown; data: { - data: { + data: Array<{ + cells: Array<{ + column: number; + row: number; + }>; table: Pick; - column: number; - value: any[]; - }; + relation?: BooleanRelation; + }>; timeFieldName?: string; negate?: boolean; }; } export function createMultiValueClickActionDefinition( - getStartServices: () => { filterManager: FilterManager } + getStartServices: () => { query: QueryStart } ): UiActionsActionDefinition { return { type: ACTION_MULTI_VALUE_CLICK, @@ -41,9 +44,23 @@ export function createMultiValueClickActionDefinition( const filters = await createFiltersFromMultiValueClickAction(context.data); return Boolean(filters); }, - execute: async (context: MultiValueClickActionContext) => { - const filter = (await createFiltersFromMultiValueClickAction(context.data)) as Filter; - getStartServices().filterManager.addFilters(filter); + execute: async ({ data }: MultiValueClickActionContext) => { + const filters = await createFiltersFromMultiValueClickAction(data); + if (!filters || filters?.length === 0) return; + const { + filterManager, + timefilter: { timefilter }, + } = getStartServices().query; + + if (data.timeFieldName) { + const { timeRangeFilter, restOfFilters } = extractTimeFilter(data.timeFieldName, filters); + filterManager.addFilters(restOfFilters); + if (timeRangeFilter) { + timefilter.setTime(convertRangeFilterToTimeRange(timeRangeFilter)); + } + } else { + filterManager.addFilters(filters); + } }, }; } diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 621b4ec7c1372..d8aa9a35a29a7 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -272,6 +272,7 @@ export type { GlobalQueryStateFromUrl, } from './query'; +// TODO: move to @kbn/search-response-warnings export type { ShardFailureRequest } from './shard_failure_modal'; export { ShardFailureOpenModalButton } from './shard_failure_modal'; diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 02d5e7f69ad51..8f4e37afb29b2 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -158,7 +158,7 @@ export class DataPublicPlugin uiActions.addTriggerAction( 'MULTI_VALUE_CLICK_TRIGGER', createMultiValueClickActionDefinition(() => ({ - filterManager: query.filterManager, + query, })) ); diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 70fa72b1634cb..1b2b0c3c78ca1 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -12,10 +12,6 @@ import { BfetchServerSetup } from '@kbn/bfetch-plugin/server'; import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/server'; -import type { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { ConfigSchema } from '../config'; import type { ISearchSetup, ISearchStart } from './search'; @@ -55,7 +51,6 @@ export interface DataPluginSetupDependencies { expressions: ExpressionsServerSetup; usageCollection?: UsageCollectionSetup; fieldFormats: FieldFormatsSetup; - taskManager?: TaskManagerSetupContract; security?: SecurityPluginSetup; } @@ -63,7 +58,6 @@ export interface DataPluginStartDependencies { fieldFormats: FieldFormatsStart; logger: Logger; dataViews: DataViewsServerPluginStart; - taskManager?: TaskManagerStartContract; } export class DataServerPlugin @@ -90,14 +84,7 @@ export class DataServerPlugin public setup( core: CoreSetup, - { - bfetch, - expressions, - usageCollection, - fieldFormats, - taskManager, - security, - }: DataPluginSetupDependencies + { bfetch, expressions, usageCollection, fieldFormats, security }: DataPluginSetupDependencies ) { this.scriptsService.setup(core); const querySetup = this.queryService.setup(core); @@ -110,7 +97,6 @@ export class DataServerPlugin expressions, usageCollection, security, - taskManager, }); return { @@ -120,14 +106,10 @@ export class DataServerPlugin }; } - public start( - core: CoreStart, - { fieldFormats, dataViews, taskManager }: DataPluginStartDependencies - ) { + public start(core: CoreStart, { fieldFormats, dataViews }: DataPluginStartDependencies) { const search = this.searchService.start(core, { fieldFormats, indexPatterns: dataViews, - taskManager, }); const datatableUtilities = new DatatableUtilitiesService( search.aggs, diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts index 8630f2be1585c..118d43ed14d25 100644 --- a/src/plugins/data/server/search/mocks.ts +++ b/src/plugins/data/server/search/mocks.ts @@ -17,6 +17,7 @@ export function createSearchSetupMock(): jest.Mocked { aggs: searchAggsSetupMock(), registerSearchStrategy: jest.fn(), searchSource: searchSourceMock.createSetupContract(), + enableRollups: jest.fn(), }; } diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index f9d1c79390fb5..0387cb820f165 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -25,10 +25,6 @@ import { ExpressionsServerSetup } from '@kbn/expressions-plugin/server'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/server'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { KbnServerError } from '@kbn/kibana-utils-plugin/server'; -import type { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import type { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import type { @@ -107,7 +103,6 @@ export interface SearchServiceSetupDependencies { bfetch: BfetchServerSetup; expressions: ExpressionsServerSetup; usageCollection?: UsageCollectionSetup; - taskManager?: TaskManagerSetupContract; security?: SecurityPluginSetup; } @@ -115,7 +110,6 @@ export interface SearchServiceSetupDependencies { export interface SearchServiceStartDependencies { fieldFormats: FieldFormatsStart; indexPatterns: DataViewsServerPluginStart; - taskManager?: TaskManagerStartContract; } /** @internal */ @@ -131,6 +125,7 @@ export class SearchService implements Plugin { private sessionService: SearchSessionService; private asScoped!: ISearchStart['asScoped']; private searchAsInternalUser!: ISearchStrategy; + private rollupsEnabled: boolean = false; constructor( private initializerContext: PluginInitializerContext, @@ -145,7 +140,7 @@ export class SearchService implements Plugin { public setup( core: CoreSetup, - { bfetch, expressions, usageCollection, taskManager, security }: SearchServiceSetupDependencies + { bfetch, expressions, usageCollection, security }: SearchServiceSetupDependencies ): ISearchSetup { core.savedObjects.registerType(searchSessionSavedObjectType); const usage = usageCollection ? usageProvider(core) : undefined; @@ -261,12 +256,13 @@ export class SearchService implements Plugin { registerSearchStrategy: this.registerSearchStrategy, usage, searchSource: this.searchSourceService.setup(), + enableRollups: () => (this.rollupsEnabled = true), }; } public start( core: CoreStart, - { fieldFormats, indexPatterns, taskManager }: SearchServiceStartDependencies + { fieldFormats, indexPatterns }: SearchServiceStartDependencies ): ISearchStart { const { elasticsearch, savedObjects, uiSettings } = core; @@ -278,7 +274,7 @@ export class SearchService implements Plugin { indexPatterns, }); - this.asScoped = this.asScopedProvider(core); + this.asScoped = this.asScopedProvider(core, this.rollupsEnabled); return { aggs, searchAsInternalUser: this.searchAsInternalUser, @@ -516,7 +512,7 @@ export class SearchService implements Plugin { return deps.searchSessionsClient.extend(sessionId, expires); }; - private asScopedProvider = (core: CoreStart) => { + private asScopedProvider = (core: CoreStart, rollupsEnabled: boolean = false) => { const { elasticsearch, savedObjects, uiSettings } = core; const getSessionAsScoped = this.sessionService.asScopedProvider(core); return (request: KibanaRequest): IScopedSearchClient => { @@ -530,6 +526,7 @@ export class SearchService implements Plugin { uiSettings.asScopedToClient(savedObjectsClient) ), request, + rollupsEnabled, }; return { search: < diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts index 070d07c07c956..627bb5fe29293 100644 --- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts +++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts @@ -64,6 +64,7 @@ describe('ES search strategy', () => { }, }, searchSessionsClient: createSearchSessionsClientMock(), + rollupsEnabled: true, } as unknown as SearchStrategyDependencies; const mockLegacyConfig$ = new BehaviorSubject({ elasticsearch: { @@ -233,6 +234,31 @@ describe('ES search strategy', () => { expect(method).toBe('POST'); expect(path).toBe('/foo-%E7%A8%8B/_rollup_search'); }); + + it("doesn't call the rollup API if the index is a rollup type BUT rollups are disabled", async () => { + mockApiCaller.mockResolvedValueOnce(mockRollupResponse); + mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse); + + const params = { index: 'foo-程', body: { query: {} } }; + const esSearch = await enhancedEsSearchStrategyProvider( + mockLegacyConfig$, + mockSearchConfig, + mockLogger + ); + + await esSearch + .search( + { + indexType: 'rollup', + params, + }, + {}, + { ...mockDeps, rollupsEnabled: false } + ) + .toPromise(); + + expect(mockApiCaller).toBeCalledTimes(0); + }); }); describe('with sessionId', () => { diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts index e5567b45f1e06..298933907b8bb 100644 --- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts +++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts @@ -154,7 +154,7 @@ export const enhancedEsSearchStrategyProvider = ( throw new KbnServerError('Unknown indexType', 400); } - if (request.indexType === undefined) { + if (request.indexType === undefined || !deps.rollupsEnabled) { return asyncSearch(request, options, deps); } else { return from(rollupSearch(request, options, deps)); diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index 50fc29334d22c..8b94085c3f80a 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -35,6 +35,7 @@ export interface SearchStrategyDependencies { uiSettingsClient: Pick; searchSessionsClient: IScopedSearchSessionsClient; request: KibanaRequest; + rollupsEnabled?: boolean; } export interface ISearchSetup { @@ -55,7 +56,7 @@ export interface ISearchSetup { * Used internally for telemetry */ usage?: SearchUsage; - + enableRollups: () => void; searchSource: ReturnType; } diff --git a/src/plugins/data/tsconfig.json b/src/plugins/data/tsconfig.json index 450c858265917..73eb71508a895 100644 --- a/src/plugins/data/tsconfig.json +++ b/src/plugins/data/tsconfig.json @@ -25,7 +25,6 @@ "@kbn/field-formats-plugin", "@kbn/data-views-plugin", "@kbn/screenshot-mode-plugin", - "@kbn/task-manager-plugin", "@kbn/security-plugin", "@kbn/expressions-plugin", "@kbn/field-types", diff --git a/src/plugins/data_views/common/data_views/data_views.test.ts b/src/plugins/data_views/common/data_views/data_views.test.ts index e6d09710fe016..2ea7726cc2bb9 100644 --- a/src/plugins/data_views/common/data_views/data_views.test.ts +++ b/src/plugins/data_views/common/data_views/data_views.test.ts @@ -629,4 +629,36 @@ describe('IndexPatterns', () => { expect(apiClient.getFieldsForWildcard.mock.calls[0][0].allowNoIndex).toBe(true); }); }); + + describe('getExistingIndices', () => { + test('getExistingIndices returns the valid matched indices', async () => { + apiClient.getFieldsForWildcard = jest + .fn() + .mockResolvedValueOnce({ fields: ['length'] }) + .mockResolvedValue({ fields: [] }); + const patternList = await indexPatterns.getExistingIndices(['packetbeat-*', 'filebeat-*']); + expect(apiClient.getFieldsForWildcard).toBeCalledTimes(2); + expect(patternList.length).toBe(1); + }); + + test('getExistingIndices checks the positive pattern if provided with a negative pattern', async () => { + const mockFn = jest.fn().mockResolvedValue({ fields: ['length'] }); + apiClient.getFieldsForWildcard = mockFn; + const patternList = await indexPatterns.getExistingIndices(['-filebeat-*', 'filebeat-*']); + expect(mockFn.mock.calls[0][0].pattern).toEqual('filebeat-*'); + expect(mockFn.mock.calls[1][0].pattern).toEqual('filebeat-*'); + expect(patternList).toEqual(['-filebeat-*', 'filebeat-*']); + }); + + test('getExistingIndices handles an error', async () => { + apiClient.getFieldsForWildcard = jest + .fn() + .mockImplementationOnce(async () => { + throw new DataViewMissingIndices('Catch me if you can!'); + }) + .mockImplementation(() => Promise.resolve({ fields: ['length'] })); + const patternList = await indexPatterns.getExistingIndices(['packetbeat-*', 'filebeat-*']); + expect(patternList).toEqual(['filebeat-*']); + }); + }); }); diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index 469fd2a1fa61e..9537526046266 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -7,10 +7,12 @@ */ import { i18n } from '@kbn/i18n'; +import { defer, from } from 'rxjs'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { FieldFormatsStartCommon, FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common'; import { v4 as uuidv4 } from 'uuid'; +import { rateLimitingForkJoin } from './utils'; import { PersistenceAPI } from '../types'; import { createDataViewCache } from '.'; @@ -236,6 +238,12 @@ export interface DataViewsServicePublicMethods { * @param options - options for getting fields */ getFieldsForWildcard: (options: GetFieldsOptions) => Promise; + /** + * Get existing index pattern list by providing string array index pattern list. + * @param indices - index pattern list + * @returns index pattern list of index patterns that match indices + */ + getExistingIndices: (indices: string[]) => Promise; /** * Get list of data view ids. * @param refresh - clear cache and fetch from server @@ -505,6 +513,41 @@ export class DataViewsService { return fields; }; + /** + * Get existing index pattern list by providing string array index pattern list. + * @param indices index pattern list + * @returns index pattern list + */ + getExistingIndices = async (indices: string[]): Promise => { + const indicesObs = indices.map((pattern) => { + // when checking a negative pattern, check if the positive pattern exists + const indexToQuery = pattern.trim().startsWith('-') + ? pattern.trim().substring(1) + : pattern.trim(); + return defer(() => + from( + this.getFieldsForWildcard({ + // check one field to keep request fast/small + fields: ['_id'], + // true so no errors thrown in browser + allowNoIndex: true, + pattern: indexToQuery, + }) + ) + ); + }); + + return new Promise((resolve) => { + rateLimitingForkJoin(indicesObs, 3, []).subscribe((value) => { + resolve(value.map((v) => v.length > 0)); + }); + }) + .then((allPatterns: boolean[]) => + indices.filter((pattern, i, self) => self.indexOf(pattern) === i && allPatterns[i]) + ) + .catch(() => indices); + }; + /** * Get field list by providing an index patttern (or spec). * @param options options for getting field list diff --git a/src/plugins/data_views/common/data_views/utils.ts b/src/plugins/data_views/common/data_views/utils.ts index a5d0447f79339..ec1c41fc3eaca 100644 --- a/src/plugins/data_views/common/data_views/utils.ts +++ b/src/plugins/data_views/common/data_views/utils.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { catchError, from, Observable, of } from 'rxjs'; +import { mergeMap, last, map, toArray } from 'rxjs/operators'; import type { RuntimeField, RuntimeFieldSpec, RuntimePrimitiveTypes } from '../types'; export const removeFieldAttrs = (runtimeField: RuntimeField): RuntimeFieldSpec => { @@ -23,3 +25,30 @@ export const removeFieldAttrs = (runtimeField: RuntimeField): RuntimeFieldSpec = ...fieldsTypeOnly, }; }; + +const MAX_CONCURRENT_REQUESTS = 3; +/** + * Helper function to run forkJoin + * with restrictions on how many input observables can be subscribed to concurrently + */ +export function rateLimitingForkJoin( + observables: Array>, + maxConcurrentRequests = MAX_CONCURRENT_REQUESTS, + failValue: T +): Observable { + return from(observables).pipe( + mergeMap( + (observable, index) => + observable.pipe( + last(), + map((value) => ({ index, value })), + catchError(() => of({ index, value: failValue })) + ), + maxConcurrentRequests + ), + toArray(), + map((indexedObservables) => + indexedObservables.sort((l, r) => l.index - r.index).map((obs) => obs.value) + ) + ); +} diff --git a/src/plugins/data_views/server/content_management/data_views_storage.ts b/src/plugins/data_views/server/content_management/data_views_storage.ts index c42a83f58e084..bd33b43a45bc1 100644 --- a/src/plugins/data_views/server/content_management/data_views_storage.ts +++ b/src/plugins/data_views/server/content_management/data_views_storage.ts @@ -31,6 +31,7 @@ export class DataViewsStorage extends SOContentStorage { 'allowNoIndex', 'name', ], + mSearchAdditionalSearchFields: ['name'], }); } } diff --git a/src/plugins/data_views/server/data_views_service_factory.ts b/src/plugins/data_views/server/data_views_service_factory.ts index fb5ae2c5afe3a..ac27ad0bc8093 100644 --- a/src/plugins/data_views/server/data_views_service_factory.ts +++ b/src/plugins/data_views/server/data_views_service_factory.ts @@ -25,6 +25,7 @@ interface DataViewsServiceFactoryDeps { uiSettings: UiSettingsServiceStart; fieldFormats: FieldFormatsStart; capabilities: CoreStart['capabilities']; + rollupsEnabled: boolean; } /** @@ -38,14 +39,18 @@ export const dataViewsServiceFactory = (deps: DataViewsServiceFactoryDeps) => request?: KibanaRequest, byPassCapabilities?: boolean ) { - const { logger, uiSettings, fieldFormats, capabilities } = deps; + const { logger, uiSettings, fieldFormats, capabilities, rollupsEnabled } = deps; const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient); return new DataViewsService({ uiSettings: new UiSettingsServerToCommon(uiSettingsClient), savedObjectsClient: new SavedObjectsClientWrapper(savedObjectsClient), - apiClient: new IndexPatternsApiServer(elasticsearchClient, savedObjectsClient), + apiClient: new IndexPatternsApiServer( + elasticsearchClient, + savedObjectsClient, + rollupsEnabled + ), fieldFormats: formats, onError: (error) => { logger.error(error); diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts index f6f2d378fef7c..6253c68d84fba 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts @@ -10,6 +10,19 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IndexPatternsFetcher } from '.'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +const rollupResponse = { + foo: { + rollup_jobs: [ + { + index_pattern: 'foo', + job_id: '123', + rollup_index: 'foo', + fields: [], + }, + ], + }, +}; + describe('Index Pattern Fetcher - server', () => { let indexPatterns: IndexPatternsFetcher; let esClient: ReturnType; @@ -21,12 +34,40 @@ describe('Index Pattern Fetcher - server', () => { beforeEach(() => { jest.clearAllMocks(); esClient = elasticsearchServiceMock.createElasticsearchClient(); - indexPatterns = new IndexPatternsFetcher(esClient); + indexPatterns = new IndexPatternsFetcher(esClient, false, true); }); it('calls fieldcaps once', async () => { esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse); - indexPatterns = new IndexPatternsFetcher(esClient, true); + indexPatterns = new IndexPatternsFetcher(esClient, true, true); await indexPatterns.getFieldsForWildcard({ pattern: patternList }); expect(esClient.fieldCaps).toHaveBeenCalledTimes(1); }); + + it('calls rollup api when given rollup data view', async () => { + esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse); + esClient.rollup.getRollupIndexCaps.mockResponse( + rollupResponse as unknown as estypes.RollupGetRollupIndexCapsResponse + ); + indexPatterns = new IndexPatternsFetcher(esClient, true, true); + await indexPatterns.getFieldsForWildcard({ + pattern: patternList, + type: 'rollup', + rollupIndex: 'foo', + }); + expect(esClient.rollup.getRollupIndexCaps).toHaveBeenCalledTimes(1); + }); + + it("doesn't call rollup api when given rollup data view and rollups are disabled", async () => { + esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse); + esClient.rollup.getRollupIndexCaps.mockResponse( + rollupResponse as unknown as estypes.RollupGetRollupIndexCapsResponse + ); + indexPatterns = new IndexPatternsFetcher(esClient, true, false); + await indexPatterns.getFieldsForWildcard({ + pattern: patternList, + type: 'rollup', + rollupIndex: 'foo', + }); + expect(esClient.rollup.getRollupIndexCaps).toHaveBeenCalledTimes(0); + }); }); diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts index 3511cefd34d87..b1d22dc5523c8 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts @@ -40,10 +40,16 @@ interface FieldSubType { export class IndexPatternsFetcher { private elasticsearchClient: ElasticsearchClient; private allowNoIndices: boolean; + private rollupsEnabled: boolean; - constructor(elasticsearchClient: ElasticsearchClient, allowNoIndices: boolean = false) { + constructor( + elasticsearchClient: ElasticsearchClient, + allowNoIndices: boolean = false, + rollupsEnabled: boolean = false + ) { this.elasticsearchClient = elasticsearchClient; this.allowNoIndices = allowNoIndices; + this.rollupsEnabled = rollupsEnabled; } /** @@ -81,7 +87,7 @@ export class IndexPatternsFetcher { fields: options.fields || ['*'], }); - if (type === 'rollup' && rollupIndex) { + if (this.rollupsEnabled && type === 'rollup' && rollupIndex) { const rollupFields: FieldDescriptor[] = []; const capabilityCheck = getCapabilitiesForRollupIndices( await this.elasticsearchClient.rollup.getRollupIndexCaps({ diff --git a/src/plugins/data_views/server/index.ts b/src/plugins/data_views/server/index.ts index 553f1a48d1d6d..33e93df3be894 100644 --- a/src/plugins/data_views/server/index.ts +++ b/src/plugins/data_views/server/index.ts @@ -10,6 +10,7 @@ export { getFieldByName, findIndexPatternById } from './utils'; export type { FieldDescriptor, RollupIndexCapability } from './fetcher'; export { IndexPatternsFetcher, getCapabilitiesForRollupIndices } from './fetcher'; export type { + DataViewsServerPluginSetup, DataViewsServerPluginStart, DataViewsServerPluginSetupDependencies, DataViewsServerPluginStartDependencies, diff --git a/src/plugins/data_views/server/index_patterns_api_client.ts b/src/plugins/data_views/server/index_patterns_api_client.ts index f470e7f8ed7df..0beb1efacf9b2 100644 --- a/src/plugins/data_views/server/index_patterns_api_client.ts +++ b/src/plugins/data_views/server/index_patterns_api_client.ts @@ -16,7 +16,8 @@ export class IndexPatternsApiServer implements IDataViewsApiClient { esClient: ElasticsearchClient; constructor( elasticsearchClient: ElasticsearchClient, - private readonly savedObjectsClient: SavedObjectsClientContract + private readonly savedObjectsClient: SavedObjectsClientContract, + private readonly rollupsEnabled: boolean ) { this.esClient = elasticsearchClient; } @@ -29,7 +30,11 @@ export class IndexPatternsApiServer implements IDataViewsApiClient { indexFilter, fields, }: GetFieldsOptions) { - const indexPatterns = new IndexPatternsFetcher(this.esClient, allowNoIndex); + const indexPatterns = new IndexPatternsFetcher( + this.esClient, + allowNoIndex, + this.rollupsEnabled + ); return await indexPatterns .getFieldsForWildcard({ pattern, diff --git a/src/plugins/data_views/server/plugin.ts b/src/plugins/data_views/server/plugin.ts index fab72338bdb33..c96de2e4294f0 100644 --- a/src/plugins/data_views/server/plugin.ts +++ b/src/plugins/data_views/server/plugin.ts @@ -33,6 +33,7 @@ export class DataViewsServerPlugin > { private readonly logger: Logger; + private rollupsEnabled: boolean = false; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get('dataView'); @@ -46,7 +47,12 @@ export class DataViewsServerPlugin core.capabilities.registerProvider(capabilitiesProvider); const dataViewRestCounter = usageCollection?.createUsageCounter('dataViewsRestApi'); - registerRoutes(core.http, core.getStartServices, dataViewRestCounter); + registerRoutes( + core.http, + core.getStartServices, + () => this.rollupsEnabled, + dataViewRestCounter + ); expressions.registerFunction(getIndexPatternLoad({ getStartServices: core.getStartServices })); registerIndexPatternsUsageCollector(core.getStartServices, usageCollection); @@ -60,7 +66,9 @@ export class DataViewsServerPlugin }, }); - return {}; + return { + enableRollups: () => (this.rollupsEnabled = true), + }; } public start( @@ -72,6 +80,7 @@ export class DataViewsServerPlugin uiSettings, fieldFormats, capabilities, + rollupsEnabled: this.rollupsEnabled, }); return { diff --git a/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts b/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts index 9951bedb82294..15d761935c0a7 100644 --- a/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts +++ b/src/plugins/data_views/server/rest_api_routes/internal/fields_for.ts @@ -111,86 +111,93 @@ const validate: FullValidationConfig = { }, }; -const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, response) => { - const { asCurrentUser } = (await context.core).elasticsearch.client; - const indexPatterns = new IndexPatternsFetcher(asCurrentUser); - const { - pattern, - meta_fields: metaFields, - type, - rollup_index: rollupIndex, - allow_no_index: allowNoIndex, - include_unmapped: includeUnmapped, - } = request.query; - - // not available to get request - const indexFilter = request.body?.index_filter; - - let parsedFields: string[] = []; - let parsedMetaFields: string[] = []; - try { - parsedMetaFields = parseFields(metaFields); - parsedFields = parseFields(request.query.fields ?? []); - } catch (error) { - return response.badRequest(); - } - - try { - const { fields, indices } = await indexPatterns.getFieldsForWildcard({ +const handler: (isRollupsEnabled: () => boolean) => RequestHandler<{}, IQuery, IBody> = + (isRollupsEnabled) => async (context, request, response) => { + const { asCurrentUser } = (await context.core).elasticsearch.client; + const indexPatterns = new IndexPatternsFetcher(asCurrentUser, undefined, isRollupsEnabled()); + const { pattern, - metaFields: parsedMetaFields, + meta_fields: metaFields, type, - rollupIndex, - fieldCapsOptions: { - allow_no_indices: allowNoIndex || false, - includeUnmapped, - }, - indexFilter, - ...(parsedFields.length > 0 ? { fields: parsedFields } : {}), - }); - - const body: { fields: FieldDescriptorRestResponse[]; indices: string[] } = { fields, indices }; - - return response.ok({ - body, - headers: { - 'content-type': 'application/json', - }, - }); - } catch (error) { - if ( - typeof error === 'object' && - !!error?.isBoom && - !!error?.output?.payload && - typeof error?.output?.payload === 'object' - ) { - const payload = error?.output?.payload; - return response.notFound({ - body: { - message: payload.message, - attributes: payload, + rollup_index: rollupIndex, + allow_no_index: allowNoIndex, + include_unmapped: includeUnmapped, + } = request.query; + + // not available to get request + const indexFilter = request.body?.index_filter; + + let parsedFields: string[] = []; + let parsedMetaFields: string[] = []; + try { + parsedMetaFields = parseFields(metaFields); + parsedFields = parseFields(request.query.fields ?? []); + } catch (error) { + return response.badRequest(); + } + + try { + const { fields, indices } = await indexPatterns.getFieldsForWildcard({ + pattern, + metaFields: parsedMetaFields, + type, + rollupIndex, + fieldCapsOptions: { + allow_no_indices: allowNoIndex || false, + includeUnmapped, + }, + indexFilter, + ...(parsedFields.length > 0 ? { fields: parsedFields } : {}), + }); + + const body: { fields: FieldDescriptorRestResponse[]; indices: string[] } = { + fields, + indices, + }; + + return response.ok({ + body, + headers: { + 'content-type': 'application/json', }, }); - } else { - return response.notFound(); + } catch (error) { + if ( + typeof error === 'object' && + !!error?.isBoom && + !!error?.output?.payload && + typeof error?.output?.payload === 'object' + ) { + const payload = error?.output?.payload; + return response.notFound({ + body: { + message: payload.message, + attributes: payload, + }, + }); + } else { + return response.notFound(); + } } - } -}; + }; -export const registerFieldForWildcard = ( +export const registerFieldForWildcard = async ( router: IRouter, getStartServices: StartServicesAccessor< DataViewsServerPluginStartDependencies, DataViewsServerPluginStart - > + >, + isRollupsEnabled: () => boolean ) => { + const configuredHandler = handler(isRollupsEnabled); + // handler - router.versioned.put({ path, access }).addVersion({ version, validate }, handler); - router.versioned.post({ path, access }).addVersion({ version, validate }, handler); + router.versioned.put({ path, access }).addVersion({ version, validate }, configuredHandler); + router.versioned.post({ path, access }).addVersion({ version, validate }, configuredHandler); router.versioned .get({ path, access }) .addVersion( { version, validate: { request: { query: querySchema }, response: validate.response } }, - handler + configuredHandler ); }; diff --git a/src/plugins/data_views/server/routes.ts b/src/plugins/data_views/server/routes.ts index ef0f342ca4fcc..9c1da30bc99e7 100644 --- a/src/plugins/data_views/server/routes.ts +++ b/src/plugins/data_views/server/routes.ts @@ -20,12 +20,13 @@ export function registerRoutes( DataViewsServerPluginStartDependencies, DataViewsServerPluginStart >, + isRollupsEnabled: () => boolean, dataViewRestCounter?: UsageCounter ) { const router = http.createRouter(); routes.forEach((route) => route(router, getStartServices, dataViewRestCounter)); - registerFieldForWildcard(router, getStartServices); + registerFieldForWildcard(router, getStartServices, isRollupsEnabled); registerHasDataViewsRoute(router); } diff --git a/src/plugins/data_views/server/types.ts b/src/plugins/data_views/server/types.ts index 5b87573322379..d595f6e04b275 100644 --- a/src/plugins/data_views/server/types.ts +++ b/src/plugins/data_views/server/types.ts @@ -53,8 +53,9 @@ export interface DataViewsServerPluginStart { /** * DataViews server plugin setup api */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DataViewsServerPluginSetup {} +export interface DataViewsServerPluginSetup { + enableRollups: () => void; +} /** * Data Views server setup dependencies diff --git a/src/plugins/discover/common/constants.ts b/src/plugins/discover/common/constants.ts index 8e61baa8ba6fc..87a9378fa963c 100644 --- a/src/plugins/discover/common/constants.ts +++ b/src/plugins/discover/common/constants.ts @@ -12,3 +12,5 @@ export enum VIEW_MODE { DOCUMENT_LEVEL = 'documents', AGGREGATED_LEVEL = 'aggregated', } + +export const DISABLE_SHARD_FAILURE_WARNING = true; diff --git a/src/plugins/discover/kibana.jsonc b/src/plugins/discover/kibana.jsonc index 67e895bd0247d..96c4aef67fe18 100644 --- a/src/plugins/discover/kibana.jsonc +++ b/src/plugins/discover/kibana.jsonc @@ -25,7 +25,8 @@ "dataViewEditor", "expressions", "unifiedSearch", - "unifiedHistogram" + "unifiedHistogram", + "contentManagement" ], "optionalPlugins": [ "home", @@ -36,13 +37,7 @@ "savedObjectsTaggingOss", "lens" ], - "requiredBundles": [ - "kibanaUtils", - "kibanaReact", - "unifiedSearch" - ], - "extraPublicDirs": [ - "common" - ] + "requiredBundles": ["kibanaUtils", "kibanaReact", "unifiedSearch"], + "extraPublicDirs": ["common"] } } diff --git a/src/plugins/discover/public/application/context/components/action_bar/action_bar.tsx b/src/plugins/discover/public/application/context/components/action_bar/action_bar.tsx index 0168e2f82e508..5cbb72f0602ee 100644 --- a/src/plugins/discover/public/application/context/components/action_bar/action_bar.tsx +++ b/src/plugins/discover/public/application/context/components/action_bar/action_bar.tsx @@ -154,6 +154,7 @@ export function ActionBar({ + {!isSuccessor && showWarning && } {!isSuccessor && showWarning && } {!isSuccessor && } diff --git a/src/plugins/discover/public/application/context/components/action_bar/action_bar_warning.tsx b/src/plugins/discover/public/application/context/components/action_bar/action_bar_warning.tsx index 65ad945429ced..d833993aadfd7 100644 --- a/src/plugins/discover/public/application/context/components/action_bar/action_bar_warning.tsx +++ b/src/plugins/discover/public/application/context/components/action_bar/action_bar_warning.tsx @@ -15,9 +15,9 @@ export function ActionBarWarning({ docCount, type }: { docCount: number; type: S if (type === SurrDocType.PREDECESSORS) { return ( [fetchedState.predecessors, fetchedState.anchor, fetchedState.successors] ); + const interceptedWarnings = useMemo( + () => + removeInterceptedWarningDuplicates([ + ...(fetchedState.predecessorsInterceptedWarnings || []), + ...(fetchedState.anchorInterceptedWarnings || []), + ...(fetchedState.successorsInterceptedWarnings || []), + ]), + [ + fetchedState.predecessorsInterceptedWarnings, + fetchedState.anchorInterceptedWarnings, + fetchedState.successorsInterceptedWarnings, + ] + ); + const addFilter = useCallback( async (field: DataViewField | string, values: unknown, operation: string) => { const newFilters = generateFilters(filterManager, field, values, operation, dataView); @@ -251,6 +266,7 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) => anchorStatus={fetchedState.anchorStatus.value} predecessorsStatus={fetchedState.predecessorsStatus.value} successorsStatus={fetchedState.successorsStatus.value} + interceptedWarnings={interceptedWarnings} /> diff --git a/src/plugins/discover/public/application/context/context_app_content.tsx b/src/plugins/discover/public/application/context/context_app_content.tsx index 940c426817a96..fa9ed3b96c9ff 100644 --- a/src/plugins/discover/public/application/context/context_app_content.tsx +++ b/src/plugins/discover/public/application/context/context_app_content.tsx @@ -8,12 +8,16 @@ import React, { useState, Fragment, useMemo, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiHorizontalRule, EuiText } from '@elastic/eui'; +import { EuiHorizontalRule, EuiSpacer, EuiText } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import { SortDirection } from '@kbn/data-plugin/public'; import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { CellActionsProvider } from '@kbn/cell-actions'; import type { DataTableRecord } from '@kbn/discover-utils/types'; +import { + SearchResponseWarnings, + type SearchResponseInterceptedWarning, +} from '@kbn/search-response-warnings'; import { CONTEXT_STEP_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '@kbn/discover-utils'; import { LoadingStatus } from './services/context_query_state'; import { ActionBar } from './components/action_bar/action_bar'; @@ -41,6 +45,7 @@ export interface ContextAppContentProps { anchorStatus: LoadingStatus; predecessorsStatus: LoadingStatus; successorsStatus: LoadingStatus; + interceptedWarnings: SearchResponseInterceptedWarning[] | undefined; useNewFieldsApi: boolean; isLegacy: boolean; setAppState: (newState: Partial) => void; @@ -71,6 +76,7 @@ export function ContextAppContent({ anchorStatus, predecessorsStatus, successorsStatus, + interceptedWarnings, useNewFieldsApi, isLegacy, setAppState, @@ -118,6 +124,16 @@ export function ContextAppContent({ return ( + {!!interceptedWarnings?.length && ( + <> + + + + )} ({ + originalWarning, +})); + const mockFilterManager = createFilterManagerMock(); +let mockOverrideInterceptedWarnings = false; + jest.mock('../services/context', () => { const originalModule = jest.requireActual('../services/context'); return { @@ -35,7 +42,12 @@ jest.mock('../services/context', () => { if (!dataView || !dataView.id) { throw new Error(); } - return type === 'predecessors' ? mockPredecessorHits : mockSuccessorHits; + return { + rows: type === 'predecessors' ? mockPredecessorHits : mockSuccessorHits, + interceptedWarnings: mockOverrideInterceptedWarnings + ? [mockInterceptedWarnings[type === 'predecessors' ? 0 : 1]] + : undefined, + }; }, }; }); @@ -45,7 +57,12 @@ jest.mock('../services/anchor', () => ({ if (!dataView.id || !anchorId) { throw new Error(); } - return mockAnchorHit; + return { + anchorRow: mockAnchorHit, + interceptedWarnings: mockOverrideInterceptedWarnings + ? [mockInterceptedWarnings[2]] + : undefined, + }; }, })); @@ -118,6 +135,9 @@ describe('test useContextAppFetch', () => { expect(result.current.fetchedState.anchor).toEqual({ ...mockAnchorHit, isAnchor: true }); expect(result.current.fetchedState.predecessors).toEqual(mockPredecessorHits); expect(result.current.fetchedState.successors).toEqual(mockSuccessorHits); + expect(result.current.fetchedState.predecessorsInterceptedWarnings).toBeUndefined(); + expect(result.current.fetchedState.successorsInterceptedWarnings).toBeUndefined(); + expect(result.current.fetchedState.anchorInterceptedWarnings).toBeUndefined(); }); it('should set anchorStatus to failed when tieBreakingField array is empty', async () => { @@ -187,4 +207,34 @@ describe('test useContextAppFetch', () => { expect(result.current.fetchedState.predecessors).toEqual([]); expect(result.current.fetchedState.successors).toEqual([]); }); + + it('should handle warnings', async () => { + mockOverrideInterceptedWarnings = true; + + const { result } = initDefaults(['_doc']); + + expect(result.current.fetchedState.anchorStatus.value).toBe(LoadingStatus.UNINITIALIZED); + expect(result.current.fetchedState.predecessorsStatus.value).toBe(LoadingStatus.UNINITIALIZED); + expect(result.current.fetchedState.successorsStatus.value).toBe(LoadingStatus.UNINITIALIZED); + + await act(async () => { + await result.current.fetchAllRows(); + }); + + expect(result.current.fetchedState.anchorStatus.value).toBe(LoadingStatus.LOADED); + expect(result.current.fetchedState.predecessorsStatus.value).toBe(LoadingStatus.LOADED); + expect(result.current.fetchedState.successorsStatus.value).toBe(LoadingStatus.LOADED); + expect(result.current.fetchedState.anchor).toEqual({ ...mockAnchorHit, isAnchor: true }); + expect(result.current.fetchedState.predecessors).toEqual(mockPredecessorHits); + expect(result.current.fetchedState.successors).toEqual(mockSuccessorHits); + expect(result.current.fetchedState.predecessorsInterceptedWarnings).toEqual([ + mockInterceptedWarnings[0], + ]); + expect(result.current.fetchedState.successorsInterceptedWarnings).toEqual([ + mockInterceptedWarnings[1], + ]); + expect(result.current.fetchedState.anchorInterceptedWarnings).toEqual([ + mockInterceptedWarnings[2], + ]); + }); }); diff --git a/src/plugins/discover/public/application/context/hooks/use_context_app_fetch.tsx b/src/plugins/discover/public/application/context/hooks/use_context_app_fetch.tsx index 0f7da6d678cfd..a2069f22c97c0 100644 --- a/src/plugins/discover/public/application/context/hooks/use_context_app_fetch.tsx +++ b/src/plugins/discover/public/application/context/hooks/use_context_app_fetch.tsx @@ -41,13 +41,8 @@ export function useContextAppFetch({ appState, useNewFieldsApi, }: ContextAppFetchProps) { - const { - uiSettings: config, - data, - toastNotifications, - filterManager, - core, - } = useDiscoverServices(); + const services = useDiscoverServices(); + const { uiSettings: config, data, toastNotifications, filterManager, core } = services; const { theme$ } = core.theme; const searchSource = useMemo(() => { @@ -95,9 +90,20 @@ export function useContextAppFetch({ { [dataView.timeFieldName!]: SortDirection.desc }, { [tieBreakerField]: SortDirection.desc }, ]; - const anchor = await fetchAnchor(anchorId, dataView, searchSource, sort, useNewFieldsApi); - setState({ anchor, anchorStatus: { value: LoadingStatus.LOADED } }); - return anchor; + const result = await fetchAnchor( + anchorId, + dataView, + searchSource, + sort, + useNewFieldsApi, + services + ); + setState({ + anchor: result.anchorRow, + anchorInterceptedWarnings: result.interceptedWarnings, + anchorStatus: { value: LoadingStatus.LOADED }, + }); + return result.anchorRow; } catch (error) { setState(createError('anchorStatus', FailureReason.UNKNOWN, error)); toastNotifications.addDanger({ @@ -106,6 +112,7 @@ export function useContextAppFetch({ }); } }, [ + services, tieBreakerField, setState, toastNotifications, @@ -124,13 +131,14 @@ export function useContextAppFetch({ type === SurrDocType.PREDECESSORS ? appState.predecessorCount : appState.successorCount; const anchor = fetchedAnchor || fetchedState.anchor; const statusKey = `${type}Status`; + const warningsKey = `${type}InterceptedWarnings`; const errorTitle = i18n.translate('discover.context.unableToLoadDocumentDescription', { defaultMessage: 'Unable to load documents', }); try { setState({ [statusKey]: { value: LoadingStatus.LOADING } }); - const rows = anchor.id + const result = anchor.id ? await fetchSurroundingDocs( type, dataView, @@ -140,10 +148,15 @@ export function useContextAppFetch({ count, filters, data, - useNewFieldsApi + useNewFieldsApi, + services ) - : []; - setState({ [type]: rows, [statusKey]: { value: LoadingStatus.LOADED } }); + : { rows: [], interceptedWarnings: undefined }; + setState({ + [type]: result.rows, + [warningsKey]: result.interceptedWarnings, + [statusKey]: { value: LoadingStatus.LOADED }, + }); } catch (error) { setState(createError(statusKey, FailureReason.UNKNOWN, error)); toastNotifications.addDanger({ @@ -155,6 +168,7 @@ export function useContextAppFetch({ } }, [ + services, filterManager, appState, fetchedState.anchor, diff --git a/src/plugins/discover/public/application/context/services/anchor.test.ts b/src/plugins/discover/public/application/context/services/anchor.test.ts index a20b2d454bcdd..415b468f38afd 100644 --- a/src/plugins/discover/public/application/context/services/anchor.test.ts +++ b/src/plugins/discover/public/application/context/services/anchor.test.ts @@ -10,7 +10,9 @@ import { SortDirection } from '@kbn/data-plugin/public'; import { createSearchSourceStub } from './_stubs'; import { fetchAnchor, updateSearchSource } from './anchor'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; +import { searchResponseTimeoutWarningMock } from '@kbn/search-response-warnings/src/__mocks__/search_response_warnings'; import { savedSearchMock } from '../../../__mocks__/saved_search'; +import { discoverServiceMock } from '../../../__mocks__/services'; describe('context app', function () { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -27,19 +29,27 @@ describe('context app', function () { }); it('should use the `fetch$` method of the SearchSource', function () { - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then(() => { + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(() => { expect(searchSourceStub.fetch$.calledOnce).toBe(true); }); }); it('should configure the SearchSource to not inherit from the implicit root', function () { - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then(() => { + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(() => { const setParentSpy = searchSourceStub.setParent; expect(setParentSpy.calledOnce).toBe(true); expect(setParentSpy.firstCall.args[0]).toBe(undefined); @@ -47,20 +57,28 @@ describe('context app', function () { }); it('should set the SearchSource data view', function () { - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then(() => { + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(() => { const setFieldSpy = searchSourceStub.setField; expect(setFieldSpy.firstCall.args[1].id).toEqual('DATA_VIEW_ID'); }); }); it('should set the SearchSource version flag to true', function () { - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then(() => { + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(() => { const setVersionSpy = searchSourceStub.setField.withArgs('version'); expect(setVersionSpy.calledOnce).toBe(true); expect(setVersionSpy.firstCall.args[1]).toEqual(true); @@ -68,10 +86,14 @@ describe('context app', function () { }); it('should set the SearchSource size to 1', function () { - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then(() => { + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(() => { const setSizeSpy = searchSourceStub.setField.withArgs('size'); expect(setSizeSpy.calledOnce).toBe(true); expect(setSizeSpy.firstCall.args[1]).toEqual(1); @@ -79,10 +101,14 @@ describe('context app', function () { }); it('should set the SearchSource query to an ids query', function () { - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then(() => { + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(() => { const setQuerySpy = searchSourceStub.setField.withArgs('query'); expect(setQuerySpy.calledOnce).toBe(true); expect(setQuerySpy.firstCall.args[1]).toEqual({ @@ -101,10 +127,14 @@ describe('context app', function () { }); it('should set the SearchSource sort order', function () { - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then(() => { + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(() => { const setSortSpy = searchSourceStub.setField.withArgs('sort'); expect(setSortSpy.calledOnce).toBe(true); expect(setSortSpy.firstCall.args[1]).toEqual([ @@ -143,10 +173,14 @@ describe('context app', function () { it('should reject with an error when no hits were found', function () { searchSourceStub = createSearchSourceStub([]); - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then( + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then( () => { fail('expected the promise to be rejected'); }, @@ -162,12 +196,53 @@ describe('context app', function () { { _id: '3', _index: 't' }, ]); - return fetchAnchor('id', dataView, searchSourceStub, [ - { '@timestamp': SortDirection.desc }, - { _doc: SortDirection.desc }, - ]).then((anchorDocument) => { - expect(anchorDocument).toHaveProperty('raw._id', '1'); - expect(anchorDocument).toHaveProperty('isAnchor', true); + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + discoverServiceMock + ).then(({ anchorRow, interceptedWarnings }) => { + expect(anchorRow).toHaveProperty('raw._id', '1'); + expect(anchorRow).toHaveProperty('isAnchor', true); + expect(interceptedWarnings).toBeUndefined(); + }); + }); + + it('should intercept shard failures', function () { + searchSourceStub = createSearchSourceStub([ + { _id: '1', _index: 't' }, + { _id: '3', _index: 't' }, + ]); + + const mockWarnings = [ + { + originalWarning: searchResponseTimeoutWarningMock, + }, + ]; + + const services = discoverServiceMock; + services.data.search.showWarnings = jest.fn((adapter, callback) => { + // @ts-expect-error for empty meta + callback?.(mockWarnings[0].originalWarning, {}); + + // plus duplicates + // @ts-expect-error for empty meta + callback?.(mockWarnings[0].originalWarning, {}); + }); + + return fetchAnchor( + 'id', + dataView, + searchSourceStub, + [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], + false, + services + ).then(({ anchorRow, interceptedWarnings }) => { + expect(anchorRow).toHaveProperty('raw._id', '1'); + expect(anchorRow).toHaveProperty('isAnchor', true); + expect(interceptedWarnings).toEqual(mockWarnings); }); }); }); @@ -185,7 +260,8 @@ describe('context app', function () { dataView, searchSourceStub, [{ '@timestamp': SortDirection.desc }, { _doc: SortDirection.desc }], - true + true, + discoverServiceMock ).then(() => { const setFieldsSpy = searchSourceStub.setField.withArgs('fields'); const removeFieldsSpy = searchSourceStub.removeField.withArgs('fieldsFromSource'); diff --git a/src/plugins/discover/public/application/context/services/anchor.ts b/src/plugins/discover/public/application/context/services/anchor.ts index dce35fea93282..daf99030201a4 100644 --- a/src/plugins/discover/public/application/context/services/anchor.ts +++ b/src/plugins/discover/public/application/context/services/anchor.ts @@ -8,19 +8,40 @@ import { lastValueFrom } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { ISearchSource, EsQuerySortValue } from '@kbn/data-plugin/public'; -import { DataView } from '@kbn/data-views-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { buildDataTableRecord } from '@kbn/discover-utils'; import type { DataTableRecord, EsHitRecord } from '@kbn/discover-utils/types'; +import { + getSearchResponseInterceptedWarnings, + type SearchResponseInterceptedWarning, +} from '@kbn/search-response-warnings'; +import type { DiscoverServices } from '../../../build_services'; +import { DISABLE_SHARD_FAILURE_WARNING } from '../../../../common/constants'; export async function fetchAnchor( anchorId: string, dataView: DataView, searchSource: ISearchSource, sort: EsQuerySortValue[], - useNewFieldsApi: boolean = false -): Promise { + useNewFieldsApi: boolean = false, + services: DiscoverServices +): Promise<{ + anchorRow: DataTableRecord; + interceptedWarnings: SearchResponseInterceptedWarning[] | undefined; +}> { updateSearchSource(searchSource, anchorId, sort, useNewFieldsApi, dataView); - const { rawResponse } = await lastValueFrom(searchSource.fetch$()); + + const adapter = new RequestAdapter(); + const { rawResponse } = await lastValueFrom( + searchSource.fetch$({ + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, + inspector: { + adapter, + title: 'anchor', + }, + }) + ); const doc = rawResponse.hits?.hits?.[0] as EsHitRecord; if (!doc) { @@ -30,7 +51,16 @@ export async function fetchAnchor( }) ); } - return buildDataTableRecord(doc, dataView, true); + return { + anchorRow: buildDataTableRecord(doc, dataView, true), + interceptedWarnings: getSearchResponseInterceptedWarnings({ + services, + adapter, + options: { + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, + }, + }), + }; } export function updateSearchSource( diff --git a/src/plugins/discover/public/application/context/services/context.predecessors.test.ts b/src/plugins/discover/public/application/context/services/context.predecessors.test.ts index cf06344eace72..451c38e87399a 100644 --- a/src/plugins/discover/public/application/context/services/context.predecessors.test.ts +++ b/src/plugins/discover/public/application/context/services/context.predecessors.test.ts @@ -14,8 +14,9 @@ import { Query } from '@kbn/es-query'; import { createContextSearchSourceStub } from './_stubs'; import { fetchSurroundingDocs, SurrDocType } from './context'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { DataTableRecord, EsHitRecord } from '@kbn/discover-utils/types'; +import type { EsHitRecord } from '@kbn/discover-utils/types'; import { buildDataTableRecord, buildDataTableRecordList } from '@kbn/discover-utils'; +import { discoverServiceMock } from '../../../__mocks__/services'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); @@ -37,7 +38,7 @@ describe('context predecessors', function () { tieBreakerField: string, tieBreakerValue: number, size: number - ) => Promise; + ) => ReturnType; // eslint-disable-next-line @typescript-eslint/no-explicit-any let mockSearchSource: any; @@ -82,7 +83,9 @@ describe('context predecessors', function () { SortDirection.desc, size, [], - dataPluginMock + dataPluginMock, + false, + discoverServiceMock ); }; }); @@ -97,9 +100,9 @@ describe('context predecessors', function () { ]; return fetchPredecessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 3).then( - (hits) => { + ({ rows }) => { expect(mockSearchSource.fetch$.calledOnce).toBe(true); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(0, 3), dataView) ); } @@ -116,7 +119,7 @@ describe('context predecessors', function () { ]; return fetchPredecessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 6).then( - (hits) => { + ({ rows }) => { const intervals: Timestamp[] = mockSearchSource.setField.args .filter(([property]: string) => property === 'query') .map(([, { query }]: [string, { query: Query }]) => @@ -131,7 +134,7 @@ describe('context predecessors', function () { // should have ended with a half-open interval expect(Object.keys(last(intervals) ?? {})).toEqual(['format', 'gte']); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(0, 3), dataView) ); } @@ -147,7 +150,7 @@ describe('context predecessors', function () { ]; return fetchPredecessors(ANCHOR_TIMESTAMP_1000, MS_PER_DAY * 1000, '_doc', 0, 3).then( - (hits) => { + ({ rows }) => { const intervals: Timestamp[] = mockSearchSource.setField.args .filter(([property]: string) => property === 'query') .map(([, { query }]: [string, { query: Query }]) => { @@ -167,7 +170,7 @@ describe('context predecessors', function () { expect(moment(last(intervals)?.lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(-3), dataView) ); } @@ -175,9 +178,11 @@ describe('context predecessors', function () { }); it('should return an empty array when no hits were found', function () { - return fetchPredecessors(ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, 3).then((hits) => { - expect(hits).toEqual([]); - }); + return fetchPredecessors(ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, 3).then( + ({ rows }) => { + expect(rows).toEqual([]); + } + ); }); it('should configure the SearchSource to not inherit from the implicit root', function () { @@ -233,7 +238,8 @@ describe('context predecessors', function () { size, [], dataPluginMock, - true + true, + discoverServiceMock ); }; }); @@ -248,13 +254,13 @@ describe('context predecessors', function () { ]; return fetchPredecessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 3).then( - (hits) => { + ({ rows }) => { const setFieldsSpy = mockSearchSource.setField.withArgs('fields'); const removeFieldsSpy = mockSearchSource.removeField.withArgs('fieldsFromSource'); expect(mockSearchSource.fetch$.calledOnce).toBe(true); expect(removeFieldsSpy.calledOnce).toBe(true); expect(setFieldsSpy.calledOnce).toBe(true); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(0, 3), dataView) ); } diff --git a/src/plugins/discover/public/application/context/services/context.successors.test.ts b/src/plugins/discover/public/application/context/services/context.successors.test.ts index 049d9efa2abf7..9c2a0120c2a72 100644 --- a/src/plugins/discover/public/application/context/services/context.successors.test.ts +++ b/src/plugins/discover/public/application/context/services/context.successors.test.ts @@ -14,8 +14,8 @@ import { createContextSearchSourceStub } from './_stubs'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { Query } from '@kbn/es-query'; import { fetchSurroundingDocs, SurrDocType } from './context'; -import type { DataTableRecord } from '@kbn/discover-utils/types'; import { buildDataTableRecord, buildDataTableRecordList } from '@kbn/discover-utils'; +import { discoverServiceMock } from '../../../__mocks__/services'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); @@ -35,7 +35,7 @@ describe('context successors', function () { tieBreakerField: string, tieBreakerValue: number, size: number - ) => Promise; + ) => ReturnType; let dataPluginMock: DataPublicPluginStart; // eslint-disable-next-line @typescript-eslint/no-explicit-any let mockSearchSource: any; @@ -83,7 +83,9 @@ describe('context successors', function () { SortDirection.desc, size, [], - dataPluginMock + dataPluginMock, + false, + discoverServiceMock ); }; }); @@ -98,9 +100,9 @@ describe('context successors', function () { ]; return fetchSuccessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 3).then( - (hits) => { + ({ rows }) => { expect(mockSearchSource.fetch$.calledOnce).toBe(true); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(-3), dataView) ); } @@ -117,7 +119,7 @@ describe('context successors', function () { ]; return fetchSuccessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 6).then( - (hits) => { + ({ rows }) => { const intervals: Timestamp[] = mockSearchSource.setField.args .filter(([property]: [string]) => property === 'query') .map(([, { query }]: [string, { query: Query }]) => @@ -132,7 +134,7 @@ describe('context successors', function () { // should have ended with a half-open interval expect(Object.keys(last(intervals) ?? {})).toEqual(['format', 'lte']); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(-3), dataView) ); } @@ -150,7 +152,7 @@ describe('context successors', function () { ]; return fetchSuccessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 4).then( - (hits) => { + ({ rows }) => { const intervals: Timestamp[] = mockSearchSource.setField.args .filter(([property]: [string]) => property === 'query') .map(([, { query }]: [string, { query: Query }]) => @@ -162,7 +164,7 @@ describe('context successors', function () { // should have stopped before reaching MS_PER_DAY * 2200 expect(moment(last(intervals)?.gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(0, 4), dataView) ); } @@ -170,8 +172,8 @@ describe('context successors', function () { }); it('should return an empty array when no hits were found', function () { - return fetchSuccessors(ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, 3).then((hits) => { - expect(hits).toEqual([]); + return fetchSuccessors(ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, 3).then(({ rows }) => { + expect(rows).toEqual([]); }); }); @@ -230,7 +232,8 @@ describe('context successors', function () { size, [], dataPluginMock, - true + true, + discoverServiceMock ); }; }); @@ -245,15 +248,104 @@ describe('context successors', function () { ]; return fetchSuccessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 3).then( - (hits) => { + ({ rows, interceptedWarnings }) => { expect(mockSearchSource.fetch$.calledOnce).toBe(true); - expect(hits).toEqual( + expect(rows).toEqual( buildDataTableRecordList(mockSearchSource._stubHits.slice(-3), dataView) ); const setFieldsSpy = mockSearchSource.setField.withArgs('fields'); const removeFieldsSpy = mockSearchSource.removeField.withArgs('fieldsFromSource'); expect(removeFieldsSpy.calledOnce).toBe(true); expect(setFieldsSpy.calledOnce).toBe(true); + expect(interceptedWarnings).toBeUndefined(); + } + ); + }); + }); + + describe('function fetchSuccessors with shard failures', function () { + const mockWarnings = [ + { + originalWarning: { + message: 'Data might be incomplete because your request timed out 1', + type: 'timed_out', + }, + }, + { + originalWarning: { + message: 'Data might be incomplete because your request timed out 2', + type: 'timed_out', + }, + }, + ]; + + beforeEach(() => { + mockSearchSource = createContextSearchSourceStub('@timestamp'); + + dataPluginMock = { + search: { + searchSource: { + createEmpty: jest.fn().mockImplementation(() => mockSearchSource), + }, + showWarnings: jest.fn((adapter, callback) => { + callback(mockWarnings[0].originalWarning, {}); + callback(mockWarnings[1].originalWarning, {}); + // plus duplicates + callback(mockWarnings[0].originalWarning, {}); + callback(mockWarnings[1].originalWarning, {}); + }), + }, + } as unknown as DataPublicPluginStart; + + fetchSuccessors = (timeValIso, timeValNr, tieBreakerField, tieBreakerValue, size) => { + const anchor = buildDataTableRecord( + { + _id: '1', + _index: 'test', + _source: { + [dataView.timeFieldName!]: timeValIso, + }, + sort: [timeValNr, tieBreakerValue], + }, + dataView, + true + ); + + return fetchSurroundingDocs( + SurrDocType.SUCCESSORS, + dataView, + anchor, + tieBreakerField, + SortDirection.desc, + size, + [], + dataPluginMock, + true, + { + ...discoverServiceMock, + data: dataPluginMock, + } + ); + }; + }); + + it('should intercept request warnings', function () { + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 5000), + mockSearchSource._createStubHit(MS_PER_DAY * 4000), + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 1), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 2), + ]; + + return fetchSuccessors(ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, 3).then( + ({ rows, interceptedWarnings }) => { + expect(mockSearchSource.fetch$.calledOnce).toBe(true); + expect(rows).toEqual( + buildDataTableRecordList(mockSearchSource._stubHits.slice(-3), dataView) + ); + expect(dataPluginMock.search.showWarnings).toHaveBeenCalledTimes(1); + expect(interceptedWarnings).toEqual(mockWarnings); } ); }); diff --git a/src/plugins/discover/public/application/context/services/context.ts b/src/plugins/discover/public/application/context/services/context.ts index ce448d68f38ef..1386af851911e 100644 --- a/src/plugins/discover/public/application/context/services/context.ts +++ b/src/plugins/discover/public/application/context/services/context.ts @@ -9,12 +9,17 @@ import type { Filter } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; import { DataPublicPluginStart, ISearchSource } from '@kbn/data-plugin/public'; import type { DataTableRecord } from '@kbn/discover-utils/types'; +import { + removeInterceptedWarningDuplicates, + type SearchResponseInterceptedWarning, +} from '@kbn/search-response-warnings'; import { reverseSortDir, SortDirection } from '../utils/sorting'; import { convertIsoToMillis, extractNanos } from '../utils/date_conversion'; import { fetchHitsInInterval } from '../utils/fetch_hits_in_interval'; import { generateIntervals } from '../utils/generate_intervals'; import { getEsQuerySearchAfter } from '../utils/get_es_query_search_after'; import { getEsQuerySort } from '../utils/get_es_query_sort'; +import type { DiscoverServices } from '../../../build_services'; export enum SurrDocType { SUCCESSORS = 'successors', @@ -36,7 +41,9 @@ const LOOKUP_OFFSETS = [0, 1, 7, 30, 365, 10000].map((days) => days * DAY_MILLIS * @param {SortDirection} sortDir - direction of sorting * @param {number} size - number of records to retrieve * @param {Filter[]} filters - to apply in the elastic query + * @param {DataPublicPluginStart} data * @param {boolean} useNewFieldsApi + * @param {DiscoverServices} services * @returns {Promise} */ export async function fetchSurroundingDocs( @@ -48,10 +55,17 @@ export async function fetchSurroundingDocs( size: number, filters: Filter[], data: DataPublicPluginStart, - useNewFieldsApi?: boolean -): Promise { + useNewFieldsApi: boolean | undefined, + services: DiscoverServices +): Promise<{ + rows: DataTableRecord[]; + interceptedWarnings: SearchResponseInterceptedWarning[] | undefined; +}> { if (typeof anchor !== 'object' || anchor === null || !size) { - return []; + return { + rows: [], + interceptedWarnings: undefined, + }; } const timeField = dataView.timeFieldName!; const searchSource = data.search.searchSource.createEmpty(); @@ -64,10 +78,11 @@ export async function fetchSurroundingDocs( nanos !== '' ? convertIsoToMillis(anchorRaw.fields?.[timeField][0]) : anchorRaw.sort?.[0]; const intervals = generateIntervals(LOOKUP_OFFSETS, timeValueMillis as number, type, sortDir); - let documents: DataTableRecord[] = []; + let rows: DataTableRecord[] = []; + let interceptedWarnings: SearchResponseInterceptedWarning[] = []; for (const interval of intervals) { - const remainingSize = size - documents.length; + const remainingSize = size - rows.length; if (remainingSize <= 0) { break; @@ -75,7 +90,7 @@ export async function fetchSurroundingDocs( const searchAfter = getEsQuerySearchAfter( type, - documents, + rows, timeField, anchor, nanos, @@ -84,7 +99,7 @@ export async function fetchSurroundingDocs( const sort = getEsQuerySort(timeField, tieBreakerField, sortDirToApply, nanos); - const hits = await fetchHitsInInterval( + const result = await fetchHitsInInterval( searchSource, timeField, sort, @@ -93,16 +108,28 @@ export async function fetchSurroundingDocs( searchAfter, remainingSize, nanos, - anchor.raw._id + anchor.raw._id, + type, + services ); - documents = + rows = type === SurrDocType.SUCCESSORS - ? [...documents, ...hits] - : [...hits.slice().reverse(), ...documents]; + ? [...rows, ...result.rows] + : [...result.rows.slice().reverse(), ...rows]; + + if (result.interceptedWarnings) { + interceptedWarnings = + type === SurrDocType.SUCCESSORS + ? [...interceptedWarnings, ...result.interceptedWarnings] + : [...result.interceptedWarnings.slice().reverse(), ...interceptedWarnings]; + } } - return documents; + return { + rows, + interceptedWarnings: removeInterceptedWarningDuplicates(interceptedWarnings), + }; } export function updateSearchSource( diff --git a/src/plugins/discover/public/application/context/services/context_query_state.ts b/src/plugins/discover/public/application/context/services/context_query_state.ts index a640c99e71e15..0b44b036be1b3 100644 --- a/src/plugins/discover/public/application/context/services/context_query_state.ts +++ b/src/plugins/discover/public/application/context/services/context_query_state.ts @@ -7,6 +7,7 @@ */ import type { DataTableRecord } from '@kbn/discover-utils/types'; +import type { SearchResponseInterceptedWarning } from '@kbn/search-response-warnings'; export interface ContextFetchState { /** @@ -33,6 +34,21 @@ export interface ContextFetchState { * Successors fetch status */ successorsStatus: LoadingStatusEntry; + + /** + * Intercepted warnings for anchor request + */ + anchorInterceptedWarnings: SearchResponseInterceptedWarning[] | undefined; + + /** + * Intercepted warnings for predecessors request + */ + predecessorsInterceptedWarnings: SearchResponseInterceptedWarning[] | undefined; + + /** + * Intercepted warnings for successors request + */ + successorsInterceptedWarnings: SearchResponseInterceptedWarning[] | undefined; } export enum LoadingStatus { @@ -60,4 +76,7 @@ export const getInitialContextQueryState = (): ContextFetchState => ({ anchorStatus: { value: LoadingStatus.UNINITIALIZED }, predecessorsStatus: { value: LoadingStatus.UNINITIALIZED }, successorsStatus: { value: LoadingStatus.UNINITIALIZED }, + anchorInterceptedWarnings: undefined, + predecessorsInterceptedWarnings: undefined, + successorsInterceptedWarnings: undefined, }); diff --git a/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts b/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts index 115d501eed135..c6fed56de4c19 100644 --- a/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts +++ b/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts @@ -10,8 +10,16 @@ import { ISearchSource, EsQuerySortValue, SortDirection } from '@kbn/data-plugin import { EsQuerySearchAfter } from '@kbn/data-plugin/common'; import { buildDataTableRecord } from '@kbn/discover-utils'; import type { DataTableRecord } from '@kbn/discover-utils/types'; +import { + getSearchResponseInterceptedWarnings, + type SearchResponseInterceptedWarning, +} from '@kbn/search-response-warnings'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { convertTimeValueToIso } from './date_conversion'; import { IntervalValue } from './generate_intervals'; +import { DISABLE_SHARD_FAILURE_WARNING } from '../../../../common/constants'; +import type { SurrDocType } from '../services/context'; +import type { DiscoverServices } from '../../../build_services'; interface RangeQuery { format: string; @@ -35,8 +43,13 @@ export async function fetchHitsInInterval( searchAfter: EsQuerySearchAfter, maxCount: number, nanosValue: string, - anchorId: string -): Promise { + anchorId: string, + type: SurrDocType, + services: DiscoverServices +): Promise<{ + rows: DataTableRecord[]; + interceptedWarnings: SearchResponseInterceptedWarning[] | undefined; +}> { const range: RangeQuery = { format: 'strict_date_optional_time', }; @@ -49,6 +62,8 @@ export async function fetchHitsInInterval( if (stop) { range[sortDir === SortDirection.asc ? 'lte' : 'gte'] = convertTimeValueToIso(stop, nanosValue); } + + const adapter = new RequestAdapter(); const fetch$ = searchSource .setField('size', maxCount) .setField('query', { @@ -75,11 +90,26 @@ export async function fetchHitsInInterval( .setField('searchAfter', searchAfter) .setField('sort', sort) .setField('version', true) - .fetch$(); + .fetch$({ + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, + inspector: { + adapter, + title: type, + }, + }); const { rawResponse } = await lastValueFrom(fetch$); const dataView = searchSource.getField('index'); - const records = rawResponse.hits?.hits.map((hit) => buildDataTableRecord(hit, dataView!)); + const rows = rawResponse.hits?.hits.map((hit) => buildDataTableRecord(hit, dataView!)); - return records ?? []; + return { + rows: rows ?? [], + interceptedWarnings: getSearchResponseInterceptedWarnings({ + services, + adapter, + options: { + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, + }, + }), + }; } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 24b0b6ba9fb89..4b2b7aa8e7125 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -20,6 +20,7 @@ import { DataView } from '@kbn/data-views-plugin/public'; import { SortOrder } from '@kbn/saved-search-plugin/public'; import { CellActionsProvider } from '@kbn/cell-actions'; import type { DataTableRecord } from '@kbn/discover-utils/types'; +import { SearchResponseWarnings } from '@kbn/search-response-warnings'; import { DOC_HIDE_TIME_COLUMN_SETTING, DOC_TABLE_LEGACY, @@ -203,6 +204,13 @@ function DiscoverDocumentsComponent({ + {!!documentState.interceptedWarnings?.length && ( + + )} {isLegacy && rows && rows.length && ( <> {!hideAnnouncements && } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index e1172a0e869d5..328153b6eeec5 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -74,6 +74,7 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { spaces, inspector, } = useDiscoverServices(); + const globalQueryState = data.query.getState(); const { main$ } = stateContainer.dataState.data$; const [query, savedQuery, columns, sort] = useAppStateSelector((state) => [ state.query, @@ -195,20 +196,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { }, [onAddColumn, draggingFieldName, currentColumns]); const mainDisplay = useMemo(() => { - if (resultState === 'none') { - const globalQueryState = data.query.getState(); - - return ( - - ); - } - if (resultState === 'uninitialized') { addLog('[DiscoverLayout] uninitialized triggers data fetching'); return stateContainer.dataState.fetch()} />; @@ -232,12 +219,9 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { ); }, [ currentColumns, - data, dataView, isPlainRecord, - isTimeBased, onAddFilter, - onDisableFilters, onFieldEdited, resultState, stateContainer, @@ -316,14 +300,25 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { - {resultState === 'none' && dataState.error ? ( - + {resultState === 'none' ? ( + dataState.error ? ( + + ) : ( + + ) ) : ( name) ?? [], + columns: savedSearchData$.documents$.getValue().textBasedQueryColumns ?? [], }); /** @@ -387,7 +386,7 @@ const createFetchCompleteObservable = (stateContainer: DiscoverStateContainer) = map(({ textBasedQueryColumns }) => ({ dataView: stateContainer.internalState.getState().dataView!, query: stateContainer.appState.getState().query!, - columns: textBasedQueryColumns?.map(({ name }) => name) ?? [], + columns: textBasedQueryColumns ?? [], })) ); }; diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx index daf7617748489..6a8f256a2357d 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx @@ -20,6 +20,7 @@ import { import { type Filter } from '@kbn/es-query'; import { DiscoverNoResults, DiscoverNoResultsProps } from './no_results'; import { createDiscoverServicesMock } from '../../../../__mocks__/services'; +import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; jest.spyOn(RxApi, 'lastValueFrom').mockImplementation(async () => ({ rawResponse: { @@ -35,9 +36,13 @@ jest.spyOn(RxApi, 'lastValueFrom').mockImplementation(async () => ({ })); async function mountAndFindSubjects( - props: Omit + props: Omit< + DiscoverNoResultsProps, + 'onDisableFilters' | 'data' | 'isTimeBased' | 'stateContainer' + > ) { const services = createDiscoverServicesMock(); + const isTimeBased = props.dataView.isTimeBased(); let component: ReactWrapper; @@ -45,7 +50,8 @@ async function mountAndFindSubjects( component = await mountWithIntl( {}} {...props} /> diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results.tsx index bd010502df149..86f73e18ca4d0 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results.tsx @@ -10,10 +10,14 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { AggregateQuery, Filter, Query } from '@kbn/es-query'; +import { SearchResponseWarnings } from '@kbn/search-response-warnings'; import { NoResultsSuggestions } from './no_results_suggestions'; +import type { DiscoverStateContainer } from '../../services/discover_state'; +import { useDataState } from '../../hooks/use_data_state'; import './_no_results.scss'; export interface DiscoverNoResultsProps { + stateContainer: DiscoverStateContainer; isTimeBased?: boolean; query: Query | AggregateQuery | undefined; filters: Filter[] | undefined; @@ -22,12 +26,26 @@ export interface DiscoverNoResultsProps { } export function DiscoverNoResults({ + stateContainer, isTimeBased, query, filters, dataView, onDisableFilters, }: DiscoverNoResultsProps) { + const { documents$ } = stateContainer.dataState.data$; + const interceptedWarnings = useDataState(documents$).interceptedWarnings; + + if (interceptedWarnings?.length) { + return ( + + ); + } + return ( diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx index c55e8de773942..633f082c4792b 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx @@ -121,6 +121,7 @@ export const NoResultsSuggestions: React.FC = ({ layout="horizontal" color="plain" icon={} + hasBorder title={

{ + .then(({ records, textBasedQueryColumns, interceptedWarnings }) => { if (services.analytics) { const duration = window.performance.now() - startTime; reportPerformanceMetricEvent(services.analytics, { @@ -131,6 +131,7 @@ export function fetchAll( fetchStatus, result: records, textBasedQueryColumns, + interceptedWarnings, recordRawType, query, }); diff --git a/src/plugins/discover/public/application/main/utils/fetch_documents.ts b/src/plugins/discover/public/application/main/utils/fetch_documents.ts index 4a4e388a27367..bce5f266d6def 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_documents.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_documents.ts @@ -11,7 +11,9 @@ import { lastValueFrom } from 'rxjs'; import { isCompleteResponse, ISearchSource } from '@kbn/data-plugin/public'; import { SAMPLE_SIZE_SETTING, buildDataTableRecordList } from '@kbn/discover-utils'; import type { EsHitRecord } from '@kbn/discover-utils/types'; +import { getSearchResponseInterceptedWarnings } from '@kbn/search-response-warnings'; import type { RecordsFetchResponse } from '../../../types'; +import { DISABLE_SHARD_FAILURE_WARNING } from '../../../../common/constants'; import { FetchDeps } from './fetch_all'; /** @@ -53,6 +55,7 @@ export const fetchDocuments = ( }), }, executionContext, + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, }) .pipe( filter((res) => isCompleteResponse(res)), @@ -61,5 +64,21 @@ export const fetchDocuments = ( }) ); - return lastValueFrom(fetch$).then((records) => ({ records })); + return lastValueFrom(fetch$).then((records) => { + const adapter = inspectorAdapters.requests; + const interceptedWarnings = adapter + ? getSearchResponseInterceptedWarnings({ + services, + adapter, + options: { + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, + }, + }) + : []; + + return { + records, + interceptedWarnings, + }; + }); }; diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 5553a8ad3b23c..8d57429440e49 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -51,6 +51,7 @@ import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/ import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { SettingsStart } from '@kbn/core-ui-settings-browser'; +import type { ContentClient } from '@kbn/content-management-plugin/public'; import { getHistory } from './kibana_services'; import { DiscoverStartPlugins } from './plugin'; import { DiscoverContextAppLocator } from './application/context/services/locator'; @@ -107,6 +108,7 @@ export interface DiscoverServices { unifiedSearch: UnifiedSearchPublicPluginStart; lens: LensPublicStart; uiActions: UiActionsStart; + contentClient: ContentClient; } export const buildServices = memoize(function ( @@ -165,5 +167,6 @@ export const buildServices = memoize(function ( unifiedSearch: plugins.unifiedSearch, lens: plugins.lens, uiActions: plugins.uiActions, + contentClient: plugins.contentManagement.client, }; }); diff --git a/src/plugins/discover/public/components/common/error_callout.tsx b/src/plugins/discover/public/components/common/error_callout.tsx index 0f1fbb722bf82..d0e914a81e851 100644 --- a/src/plugins/discover/public/components/common/error_callout.tsx +++ b/src/plugins/discover/public/components/common/error_callout.tsx @@ -10,9 +10,6 @@ import { EuiButton, EuiCallOut, EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, EuiLink, EuiModal, EuiModalBody, @@ -104,36 +101,31 @@ export const ErrorCallout = ({ /> ) : ( - - - - -

{formattedTitle}

-
- - } + title={

{formattedTitle}

} body={ - overrideDisplay?.body ?? ( - <> -

- {error.message} -

- {showErrorMessage} - - ) +
+ {overrideDisplay?.body ?? ( + <> +

+ {error.message} +

+ {showErrorMessage} + + )} +
} - css={css` - text-align: left; - `} data-test-subj={dataTestSubj} /> )} diff --git a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx index e45faec8cbaa1..570c980e649e5 100644 --- a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx +++ b/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx @@ -33,6 +33,7 @@ export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps) sharedItemTitle={renderProps.sharedItemTitle} isLoading={renderProps.isLoading} isPlainRecord={renderProps.isPlainRecord} + interceptedWarnings={renderProps.interceptedWarnings} dataTestSubj="embeddedSavedSearchDocTable" DocViewer={DocViewer} /> diff --git a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx index 97ed5f3af9d14..6901df855984e 100644 --- a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx +++ b/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx @@ -11,6 +11,7 @@ import './index.scss'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiText } from '@elastic/eui'; import { SAMPLE_SIZE_SETTING, usePager } from '@kbn/discover-utils'; +import type { SearchResponseInterceptedWarning } from '@kbn/search-response-warnings'; import { ToolBarPagination, MAX_ROWS_PER_PAGE_OPTION, @@ -22,6 +23,7 @@ import { SavedSearchEmbeddableBase } from '../../embeddable/saved_search_embedda export interface DocTableEmbeddableProps extends DocTableProps { totalHitCount: number; rowsPerPageState?: number; + interceptedWarnings?: SearchResponseInterceptedWarning[]; onUpdateRowsPerPage?: (rowsPerPage?: number) => void; } @@ -101,6 +103,7 @@ export const DocTableEmbeddable = (props: DocTableEmbeddableProps) => { return ( & filter?: (field: DataViewField, value: string[], operator: string) => void; hits?: DataTableRecord[]; totalHitCount?: number; + interceptedWarnings?: SearchResponseInterceptedWarning[]; onMoveColumn?: (column: string, index: number) => void; onUpdateRowHeight?: (rowHeight?: number) => void; onUpdateRowsPerPage?: (rowsPerPage?: number) => void; @@ -279,6 +284,7 @@ export class SavedSearchEmbeddable this.inspectorAdapters.requests!.reset(); searchProps.isLoading = true; + searchProps.interceptedWarnings = undefined; const wasAlreadyRendered = this.getOutput().rendered; @@ -357,9 +363,20 @@ export class SavedSearchEmbeddable }), }, executionContext, + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, }) ); + if (this.inspectorAdapters.requests) { + searchProps.interceptedWarnings = getSearchResponseInterceptedWarnings({ + services: this.services, + adapter: this.inspectorAdapters.requests, + options: { + disableShardFailureWarning: DISABLE_SHARD_FAILURE_WARNING, + }, + }); + } + this.updateOutput({ ...this.getOutput(), loading: false, diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable_badge.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable_badge.tsx new file mode 100644 index 0000000000000..9944adb4be33c --- /dev/null +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable_badge.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { + SearchResponseWarnings, + type SearchResponseInterceptedWarning, +} from '@kbn/search-response-warnings'; + +export interface SavedSearchEmbeddableBadgeProps { + interceptedWarnings: SearchResponseInterceptedWarning[] | undefined; +} + +export const SavedSearchEmbeddableBadge: React.FC = ({ + interceptedWarnings, +}) => { + return interceptedWarnings?.length ? ( + + ) : null; +}; diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable_base.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable_base.tsx index 17785570b9487..b41c70676c754 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable_base.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable_base.tsx @@ -9,7 +9,9 @@ import React from 'react'; import { css } from '@emotion/react'; import { EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; +import type { SearchResponseInterceptedWarning } from '@kbn/search-response-warnings'; import { TotalDocuments } from '../application/main/components/total_documents/total_documents'; +import { SavedSearchEmbeddableBadge } from './saved_search_embeddable_badge'; const containerStyles = css` width: 100%; @@ -22,6 +24,7 @@ export interface SavedSearchEmbeddableBaseProps { prepend?: React.ReactElement; append?: React.ReactElement; dataTestSubj?: string; + interceptedWarnings?: SearchResponseInterceptedWarning[]; } export const SavedSearchEmbeddableBase: React.FC = ({ @@ -30,6 +33,7 @@ export const SavedSearchEmbeddableBase: React.FC prepend, append, dataTestSubj, + interceptedWarnings, children, }) => { return ( @@ -62,6 +66,12 @@ export const SavedSearchEmbeddableBase: React.FC {children} {Boolean(append) && {append}} + + {Boolean(interceptedWarnings?.length) && ( +
+ +
+ )} ); }; diff --git a/src/plugins/discover/public/embeddable/saved_search_grid.tsx b/src/plugins/discover/public/embeddable/saved_search_grid.tsx index 075a3ca930235..87258347b474e 100644 --- a/src/plugins/discover/public/embeddable/saved_search_grid.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_grid.tsx @@ -7,6 +7,7 @@ */ import React, { useState, memo } from 'react'; import type { DataTableRecord } from '@kbn/discover-utils/types'; +import type { SearchResponseInterceptedWarning } from '@kbn/search-response-warnings'; import { DiscoverGrid, DiscoverGridProps } from '../components/discover_grid/discover_grid'; import './saved_search_grid.scss'; import { DiscoverGridFlyout } from '../components/discover_grid/discover_grid_flyout'; @@ -14,11 +15,13 @@ import { SavedSearchEmbeddableBase } from './saved_search_embeddable_base'; export interface DiscoverGridEmbeddableProps extends DiscoverGridProps { totalHitCount: number; + interceptedWarnings?: SearchResponseInterceptedWarning[]; } export const DataGridMemoized = memo(DiscoverGrid); export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) { + const { interceptedWarnings, ...gridProps } = props; const [expandedDoc, setExpandedDoc] = useState(undefined); return ( @@ -26,9 +29,10 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) { totalHitCount={props.totalHitCount} isLoading={props.isLoading} dataTestSubj="embeddedSavedSearchDocTable" + interceptedWarnings={props.interceptedWarnings} > { 'testId', 'CONTACT_CARD_EMBEDDABLE', 'test name', - {} as unknown as SavedObjectCommon + {} as unknown as SavedObjectCommon ) } > diff --git a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx index 82ee19cfaa2c0..07765836057ff 100644 --- a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx @@ -24,7 +24,7 @@ import { embeddableStart, usageCollection, savedObjectsTaggingOss, - savedObjectsManagement, + contentManagement, } from '../kibana_services'; import { IContainer, @@ -85,7 +85,7 @@ export const AddPanelFlyout = ({ (embeddableFactory) => Boolean(embeddableFactory.savedObjectMetaData) && !embeddableFactory.isContainerType ) - .map(({ savedObjectMetaData }) => savedObjectMetaData as SavedObjectMetaData), + .map(({ savedObjectMetaData }) => savedObjectMetaData as SavedObjectMetaData), [factoriesBySavedObjectType] ); @@ -125,10 +125,9 @@ export const AddPanelFlyout = ({ { @@ -45,6 +46,7 @@ export const setKibanaServices = ( usageCollection = deps.usageCollection; savedObjectsManagement = deps.savedObjectsManagement; savedObjectsTaggingOss = deps.savedObjectsTaggingOss; + contentManagement = deps.contentManagement; servicesReady$.next(true); }; diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts index 94c09c2d79376..dbafad289c006 100644 --- a/src/plugins/embeddable/public/lib/triggers/triggers.ts +++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { Datatable, DatatableColumnMeta } from '@kbn/expressions-plugin/common'; import { Trigger, RowClickContext } from '@kbn/ui-actions-plugin/public'; +import { BooleanRelation } from '@kbn/es-query'; import { IEmbeddable } from '..'; export interface EmbeddableContext { @@ -32,11 +33,14 @@ export interface ValueClickContext { export interface MultiValueClickContext { embeddable?: T; data: { - data: { + data: Array<{ table: Pick; - column: number; - value: any[]; - }; + cells: Array<{ + column: number; + row: number; + }>; + relation?: BooleanRelation; + }>; timeFieldName?: string; negate?: boolean; }; @@ -157,12 +161,27 @@ export const cellValueTrigger: Trigger = { export const isValueClickTriggerContext = ( context: ChartActionContext -): context is ValueClickContext => context.data && 'data' in context.data; +): context is ValueClickContext => { + return ( + context.data && + 'data' in context.data && + Array.isArray(context.data.data) && + context.data.data.length > 0 && + 'column' in context.data.data[0] + ); +}; export const isMultiValueClickTriggerContext = ( context: ChartActionContext -): context is MultiValueClickContext => - context.data && 'data' in context.data && !Array.isArray(context.data.data); +): context is MultiValueClickContext => { + return ( + context.data && + 'data' in context.data && + Array.isArray(context.data.data) && + context.data.data.length > 0 && + 'cells' in context.data.data[0] + ); +}; export const isRangeSelectTriggerContext = ( context: ChartActionContext diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx index ef5ed38c82571..ec33b708d891d 100644 --- a/src/plugins/embeddable/public/mocks.tsx +++ b/src/plugins/embeddable/public/mocks.tsx @@ -16,6 +16,7 @@ import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { type AggregateQuery, type Filter, type Query } from '@kbn/es-query'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; import { EmbeddableStart, @@ -141,6 +142,8 @@ const createInstance = (setupPlugins: Partial = {}) savedObjectsManagement: savedObjectsManagementMock as unknown as SavedObjectsManagementPluginStart, usageCollection: { reportUiCounter: jest.fn() }, + contentManagement: + startPlugins.contentManagement || contentManagementMock.createStartContract(), }); return { plugin, @@ -167,5 +170,6 @@ export const setStubKibanaServices = () => { inspector: inspectorPluginMock.createStartContract(), savedObjectsManagement: savedObjectsManagementPluginMock.createStartContract(), usageCollection: { reportUiCounter: jest.fn() }, + contentManagement: contentManagementMock.createStartContract(), }); }; diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index df18c8e472457..89cd065d39cc3 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -23,6 +23,7 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import { migrateToLatest, PersistableStateService } from '@kbn/kibana-utils-plugin/common'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { EmbeddableFactoryRegistry, @@ -65,6 +66,7 @@ export interface EmbeddableStartDependencies { uiActions: UiActionsStart; inspector: InspectorStart; usageCollection: UsageCollectionStart; + contentManagement: ContentManagementPublicStart; savedObjectsManagement: SavedObjectsManagementPluginStart; savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; } diff --git a/src/plugins/embeddable/public/tests/test_plugin.ts b/src/plugins/embeddable/public/tests/test_plugin.ts index 75792ab5fde3e..ee6dcdd36722b 100644 --- a/src/plugins/embeddable/public/tests/test_plugin.ts +++ b/src/plugins/embeddable/public/tests/test_plugin.ts @@ -17,6 +17,7 @@ import { } from '@kbn/saved-objects-management-plugin/public'; import { Query } from '@kbn/es-query'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; import { EmbeddablePublicPlugin, EmbeddableSetup, EmbeddableStart } from '../plugin'; export interface TestPluginReturn { plugin: EmbeddablePublicPlugin; @@ -66,6 +67,7 @@ export const testPlugin = ( savedObjectsManagement: savedObjectsManagementMock as unknown as SavedObjectsManagementPluginStart, usageCollection: { reportUiCounter: jest.fn() }, + contentManagement: contentManagementMock.createStartContract(), }); return start; }, diff --git a/src/plugins/embeddable/tsconfig.json b/src/plugins/embeddable/tsconfig.json index 6470017e62220..80bc9afbc906f 100644 --- a/src/plugins/embeddable/tsconfig.json +++ b/src/plugins/embeddable/tsconfig.json @@ -29,7 +29,8 @@ "@kbn/analytics", "@kbn/usage-collection-plugin", "@kbn/ui-theme", - "@kbn/core-mount-utils-browser" + "@kbn/core-mount-utils-browser", + "@kbn/content-management-plugin" ], "exclude": ["target/**/*"] } diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index d343ecd76b4dc..315db78b8a68c 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -9,7 +9,6 @@ "browser": true, "requiredPlugins": [ "expressions", - "savedObjectsManagement", "data", "presentationUtil", "visualizations", diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx index 4a969b210ef82..d9e97b0b2870c 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx +++ b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx @@ -8,12 +8,11 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { CoreStart } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; -import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import type { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentClient } from '@kbn/content-management-plugin/public'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { EuiButton, EuiEmptyPrompt, @@ -26,24 +25,22 @@ import { css } from '@emotion/react'; import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-common'; export const EventAnnotationGroupSavedObjectFinder = ({ + contentClient, uiSettings, - http, - savedObjectsManagement, fixedPageSize = 10, checkHasAnnotationGroups, onChoose, onCreateNew, }: { uiSettings: IUiSettingsClient; - http: CoreStart['http']; - savedObjectsManagement: SavedObjectsManagementPluginStart; + contentClient: ContentClient; fixedPageSize?: number; checkHasAnnotationGroups: () => Promise; onChoose: (value: { id: string; type: string; fullName: string; - savedObject: SavedObjectCommon; + savedObject: SavedObjectCommon; }) => void; onCreateNew: () => void; }) => { @@ -114,11 +111,7 @@ export const EventAnnotationGroupSavedObjectFinder = ({ /> } savedObjectMetaData={savedObjectMetaData} - services={{ - uiSettings, - http, - savedObjectsManagement, - }} + services={{ contentClient, uiSettings }} /> ); }; diff --git a/src/plugins/event_annotation/public/event_annotation_service/index.tsx b/src/plugins/event_annotation/public/event_annotation_service/index.tsx index 0bc2f9e918038..d950e8af198ff 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/index.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/index.tsx @@ -7,7 +7,6 @@ */ import { CoreStart } from '@kbn/core/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-components'; export type { EventAnnotationServiceType }; @@ -16,27 +15,17 @@ export class EventAnnotationService { private eventAnnotationService?: EventAnnotationServiceType; private core: CoreStart; - private savedObjectsManagement: SavedObjectsManagementPluginStart; private contentManagement: ContentManagementPublicStart; - constructor( - core: CoreStart, - contentManagement: ContentManagementPublicStart, - savedObjectsManagement: SavedObjectsManagementPluginStart - ) { + constructor(core: CoreStart, contentManagement: ContentManagementPublicStart) { this.core = core; this.contentManagement = contentManagement; - this.savedObjectsManagement = savedObjectsManagement; } public async getService() { if (!this.eventAnnotationService) { const { getEventAnnotationService } = await import('./service'); - this.eventAnnotationService = getEventAnnotationService( - this.core, - this.contentManagement, - this.savedObjectsManagement - ); + this.eventAnnotationService = getEventAnnotationService(this.core, this.contentManagement); } return this.eventAnnotationService; } diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index 6463f14441995..c122102ae632a 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -9,7 +9,6 @@ import { CoreStart, SimpleSavedObject } from '@kbn/core/public'; import { ContentClient, ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { coreMock } from '@kbn/core/public/mocks'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { EventAnnotationConfig } from '@kbn/event-annotation-common'; import { getEventAnnotationService } from './service'; import { EventAnnotationServiceType } from '@kbn/event-annotation-components'; @@ -161,11 +160,9 @@ describe('Event Annotation Service', () => { hits: Object.values(annotationGroupResolveMocks), }); (contentClient.delete as jest.Mock).mockResolvedValue({}); - eventAnnotationService = getEventAnnotationService( - core, - { client: contentClient } as ContentManagementPublicStart, - {} as SavedObjectsManagementPluginStart - ); + eventAnnotationService = getEventAnnotationService(core, { + client: contentClient, + } as ContentManagementPublicStart); }); afterEach(() => { jest.clearAllMocks(); diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index c520f02d37dbd..b45748ed06eaf 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -11,7 +11,6 @@ import { partition } from 'lodash'; import { queryToAst } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import type { CoreStart, SavedObjectReference } from '@kbn/core/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { type EventAnnotationServiceType } from '@kbn/event-annotation-components'; @@ -48,8 +47,7 @@ export function hasIcon(icon: string | undefined): icon is string { export function getEventAnnotationService( core: CoreStart, - contentManagement: ContentManagementPublicStart, - savedObjectsManagement: SavedObjectsManagementPluginStart + contentManagement: ContentManagementPublicStart ): EventAnnotationServiceType { const client = contentManagement.client; @@ -288,9 +286,8 @@ export function getEventAnnotationService( renderEventAnnotationGroupSavedObjectFinder: (props) => { return ( diff --git a/src/plugins/event_annotation/public/mocks.ts b/src/plugins/event_annotation/public/mocks.ts index f0e4dc34b876c..605f16f87c97b 100644 --- a/src/plugins/event_annotation/public/mocks.ts +++ b/src/plugins/event_annotation/public/mocks.ts @@ -7,20 +7,15 @@ */ import { coreMock } from '@kbn/core/public/mocks'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { getEventAnnotationService } from './event_annotation_service/service'; // not really mocking but avoiding async loading -export const eventAnnotationServiceMock = getEventAnnotationService( - coreMock.createStart(), - { - client: { - get: jest.fn(), - search: jest.fn(), - create: jest.fn(), - update: jest.fn(), - }, - } as unknown as ContentManagementPublicStart, - {} as SavedObjectsManagementPluginStart -); +export const eventAnnotationServiceMock = getEventAnnotationService(coreMock.createStart(), { + client: { + get: jest.fn(), + search: jest.fn(), + create: jest.fn(), + update: jest.fn(), + }, +} as unknown as ContentManagementPublicStart); diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index b58423b790b3d..bb1485210835f 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -11,7 +11,6 @@ import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/ import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { ContentManagementPublicSetup, ContentManagementPublicStart, @@ -34,7 +33,6 @@ import { ANNOTATIONS_LISTING_VIEW_ID } from '../common/constants'; import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; export interface EventAnnotationStartDependencies { - savedObjectsManagement: SavedObjectsManagementPluginStart; data: DataPublicPluginStart; savedObjectsTagging: SavedObjectTaggingPluginStart; presentationUtil: PresentationUtilPluginStart; @@ -89,8 +87,7 @@ export class EventAnnotationPlugin const eventAnnotationService = await new EventAnnotationService( coreStart, - pluginsStart.contentManagement, - pluginsStart.savedObjectsManagement + pluginsStart.contentManagement ).getService(); const ids = await pluginsStart.dataViews.getIds(); @@ -125,10 +122,6 @@ export class EventAnnotationPlugin core: CoreStart, startDependencies: EventAnnotationStartDependencies ): EventAnnotationService { - return new EventAnnotationService( - core, - startDependencies.contentManagement, - startDependencies.savedObjectsManagement - ); + return new EventAnnotationService(core, startDependencies.contentManagement); } } diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index a14cebb9541e6..0c279bcd92436 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -19,7 +19,6 @@ "@kbn/core-ui-settings-browser", "@kbn/datemath", "@kbn/saved-objects-finder-plugin", - "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-tagging-plugin", "@kbn/presentation-util-plugin", "@kbn/visualizations-plugin", diff --git a/src/plugins/files/server/routes/file_kind/download.ts b/src/plugins/files/server/routes/file_kind/download.ts index 854a520a69427..842bf5744956a 100644 --- a/src/plugins/files/server/routes/file_kind/download.ts +++ b/src/plugins/files/server/routes/file_kind/download.ts @@ -59,6 +59,7 @@ export function register(fileKindRouter: FileKindRouter, fileKind: FileKind) { validate: { ...rt }, options: { tags: fileKind.http.download.tags, + access: 'public', // the endpoint is used by and should work without any special headers }, }, handler diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index 7a4958099931f..c335f56998ce3 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -156,6 +156,7 @@ export const applicationUsageSchema = { monitoring: commonSchema, 'observability-overview': commonSchema, observabilityOnboarding: commonSchema, + observabilityAIAssistant: commonSchema, 'exploratory-view': commonSchema, osquery: commonSchema, profiling: commonSchema, diff --git a/src/plugins/saved_objects_finder/common/index.ts b/src/plugins/saved_objects_finder/common/index.ts index 8a0964edc100b..6b80798a41cc9 100644 --- a/src/plugins/saved_objects_finder/common/index.ts +++ b/src/plugins/saved_objects_finder/common/index.ts @@ -7,4 +7,4 @@ */ export { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '@kbn/saved-objects-settings'; -export type { SavedObjectCommon, FindQueryHTTP, FindResponseHTTP, FinderAttributes } from './types'; +export type { FinderAttributes, SavedObjectCommon } from './types'; diff --git a/src/plugins/saved_objects_finder/common/types.ts b/src/plugins/saved_objects_finder/common/types.ts index 1575da09698fb..60ff52f9aedc3 100644 --- a/src/plugins/saved_objects_finder/common/types.ts +++ b/src/plugins/saved_objects_finder/common/types.ts @@ -5,32 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { SavedObject } from '@kbn/core-saved-objects-server'; +import type { SOWithMetadata } from '@kbn/content-management-utils'; -export type SavedObjectCommon = SavedObject; - -export interface FindQueryHTTP { - perPage?: number; - page?: number; - type: string | string[]; - search?: string; - searchFields?: string[]; - defaultSearchOperator?: 'AND' | 'OR'; - sortField?: string; - sortOrder?: 'asc' | 'desc'; - fields?: string | string[]; - hasReference?: string; -} +export type SavedObjectCommon = SOWithMetadata; export interface FinderAttributes { title?: string; name?: string; - type: string; -} - -export interface FindResponseHTTP { - saved_objects: Array>; - total: number; - page: number; - per_page: number; } diff --git a/src/plugins/saved_objects_finder/kibana.jsonc b/src/plugins/saved_objects_finder/kibana.jsonc index dbfc1e8838161..ad53ee32ae369 100644 --- a/src/plugins/saved_objects_finder/kibana.jsonc +++ b/src/plugins/saved_objects_finder/kibana.jsonc @@ -6,5 +6,6 @@ "id": "savedObjectsFinder", "server": true, "browser": true, + "requiredBundles": ["savedObjectsManagement"] } } diff --git a/src/plugins/saved_objects_finder/public/finder/index.tsx b/src/plugins/saved_objects_finder/public/finder/index.tsx index 93f61401dbe0a..3d991ee7fd4dd 100644 --- a/src/plugins/saved_objects_finder/public/finder/index.tsx +++ b/src/plugins/saved_objects_finder/public/finder/index.tsx @@ -8,10 +8,9 @@ import { EuiDelayRender, EuiSkeletonText } from '@elastic/eui'; import React from 'react'; -import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; -import { HttpStart } from '@kbn/core-http-browser'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import type { ContentClient } from '@kbn/content-management-plugin/public'; +import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import type { SavedObjectFinderProps } from './saved_object_finder'; const LazySavedObjectFinder = React.lazy(() => import('./saved_object_finder')); @@ -28,16 +27,12 @@ const SavedObjectFinder = (props: SavedObjectFinderProps) => ( ); export const getSavedObjectFinder = ( + contentClient: ContentClient, uiSettings: IUiSettingsClient, - http: HttpStart, - savedObjectsManagement: SavedObjectsManagementPluginStart, savedObjectsTagging?: SavedObjectsTaggingApi ) => { return (props: SavedObjectFinderProps) => ( - + ); }; diff --git a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx index 674e49a77c360..f02517e845ed9 100644 --- a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx +++ b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.test.tsx @@ -23,11 +23,10 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; import * as sinon from 'sinon'; import { SavedObjectFinderUi as SavedObjectFinder } from './saved_object_finder'; -import { coreMock } from '@kbn/core/public/mocks'; -import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; import { findTestSubject } from '@kbn/test-jest-helpers'; -import { SavedObjectManagementTypeInfo } from '@kbn/saved-objects-management-plugin/public'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { coreMock } from '@kbn/core/public/mocks'; describe('SavedObjectsFinder', () => { const doc = { @@ -67,23 +66,14 @@ describe('SavedObjectsFinder', () => { }, ]; - const savedObjectsManagement = savedObjectsManagementPluginMock.createStartContract(); - savedObjectsManagement.parseQuery.mockImplementation( - (query: Query, types: SavedObjectManagementTypeInfo[]) => { - const queryTypes = query.ast.getFieldClauses('type')?.[0].value as string[] | undefined; - return { - queryText: query.ast - .getTermClauses() - .map((clause: any) => clause.value) - .join(' '), - visibleTypes: queryTypes?.filter((name) => types.some((type) => type.name === name)), - selectedTags: query.ast.getFieldClauses('tag')?.[0].value as string[] | undefined, - }; - } - ); - savedObjectsManagement.getTagFindReferences.mockImplementation( - ({ selectedTags }) => selectedTags as any - ); + const contentManagement = contentManagementMock.createStartContract(); + const contentClient = contentManagement.client; + beforeEach(() => { + (contentClient.mSearch as any as jest.SpyInstance).mockClear(); + }); + const coreStart = coreMock.createStart(); + const uiSettings = coreStart.uiSettings; + uiSettings.get.mockImplementation(() => 10); const savedObjectsTagging = { ui: { @@ -102,22 +92,20 @@ describe('SavedObjectsFinder', () => { multiSelect: 'or', options: [], })), + convertNameToReference: jest.fn((name: string) => ({ type: 'tag', id: name })), }, } as any as SavedObjectsTaggingApi; it('should call API on startup', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = shallow( { wrapper.instance().componentDidMount!(); await nextTick(); - expect(core.http.get).toHaveBeenCalledWith('/internal/saved-objects-finder/find', { - query: { - type: ['search'], - fields: ['title', 'name'], - search: undefined, - hasReference: undefined, - page: 1, - perPage: 10, - searchFields: ['title^3', 'description', 'name'], - defaultSearchOperator: 'AND', - }, + expect(contentClient.mSearch).toHaveBeenCalledWith({ + contentTypes: [{ contentTypeId: 'search' }], + query: { limit: 10 }, }); }); describe('render', () => { it('lists initial items', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = shallow( ); @@ -172,20 +145,13 @@ describe('SavedObjectsFinder', () => { it('calls onChoose on item click', async () => { const chooseStub = sinon.stub(); - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -201,20 +167,13 @@ describe('SavedObjectsFinder', () => { }); it('with help text', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = shallow( @@ -228,20 +187,13 @@ describe('SavedObjectsFinder', () => { }); it('with left button', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc] }) ); - core.uiSettings.get.mockImplementation(() => 10); const button = Hello; const wrapper = shallow( @@ -268,20 +220,13 @@ describe('SavedObjectsFinder', () => { describe('sorting', () => { it('should list items by type ascending', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc3, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc3, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -299,20 +244,13 @@ describe('SavedObjectsFinder', () => { }); it('should list items by type descending', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc3, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc3, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -334,20 +272,13 @@ describe('SavedObjectsFinder', () => { }); it('should list items by title ascending', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -361,20 +292,13 @@ describe('SavedObjectsFinder', () => { }); it('should list items by title descending', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -391,20 +315,13 @@ describe('SavedObjectsFinder', () => { }); it('should list items by tag ascending', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc3, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc3, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -422,20 +339,13 @@ describe('SavedObjectsFinder', () => { }); it('should list items by tag descending', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc3, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc3, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -458,20 +368,13 @@ describe('SavedObjectsFinder', () => { }); it('should not show the saved objects which get filtered by showSavedObject', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = shallow( { describe('search', () => { it('should request filtered list on search input', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -515,84 +411,25 @@ describe('SavedObjectsFinder', () => { wrapper .find('[data-test-subj="savedObjectFinderSearchInput"] input') .simulate('keyup', { key: 'Enter', target: { value: 'abc' } }); - expect(core.http.get).toHaveBeenCalledWith('/internal/saved-objects-finder/find', { - query: { - type: ['search'], - fields: ['title', 'name'], - search: 'abc*', - hasReference: undefined, - page: 1, - perPage: 10, - searchFields: ['title^3', 'description', 'name'], - defaultSearchOperator: 'AND', - }, - }); - }); - - it('should include additional fields in search if listed in meta data', async () => { - const core = coreMock.createStart(); - (core.http.get as jest.Mock).mockResolvedValue({ saved_objects: [] }); - core.uiSettings.get.mockImplementation(() => 10); - - const wrapper = mount( - 'search', - includeFields: ['field1', 'field2'], - }, - { - type: 'type2', - name: '', - getIconForSavedObject: () => 'search', - includeFields: ['field2', 'field3'], - }, - ]} - /> - ); - wrapper.instance().componentDidMount!(); - await nextTick(); - wrapper - .find('[data-test-subj="savedObjectFinderSearchInput"] input') - .simulate('keyup', { key: 'Enter', target: { value: 'abc' } }); - expect(core.http.get).toHaveBeenCalledWith('/internal/saved-objects-finder/find', { - query: { - type: ['type1', 'type2'], - fields: ['title', 'name', 'field1', 'field2', 'field3'], - search: 'abc*', - hasReference: undefined, - page: 1, - perPage: 10, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', - }, + expect(contentClient.mSearch).toHaveBeenCalledWith({ + contentTypes: [ + { + contentTypeId: 'search', + }, + ], + query: { limit: 10, text: 'abc*' }, }); }); it('should respect response order on search input', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( ); @@ -609,20 +446,13 @@ describe('SavedObjectsFinder', () => { }); it('should request multiple saved object types at once', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = shallow( { wrapper.instance().componentDidMount!(); - expect(core.http.get).toHaveBeenCalledWith('/internal/saved-objects-finder/find', { - query: { - type: ['search', 'vis'], - fields: ['title', 'name'], - search: undefined, - page: 1, - perPage: 10, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', - }, + expect(contentClient.mSearch).toHaveBeenCalledWith({ + contentTypes: [ + { + contentTypeId: 'search', + }, + { + contentTypeId: 'vis', + }, + ], + query: { limit: 10, text: undefined }, }); }); describe('filter', () => { it('should render filter buttons if enabled', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ - saved_objects: [doc, doc2, doc3], - }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2, doc3] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -684,22 +505,13 @@ describe('SavedObjectsFinder', () => { }); it('should not render filter buttons if disabled', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ - saved_objects: [doc, doc2, doc3], - }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2, doc3] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -711,22 +523,13 @@ describe('SavedObjectsFinder', () => { }); it('should not render types filter button if there is only one type in the metadata list', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ - saved_objects: [doc, doc2], - }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -739,22 +542,13 @@ describe('SavedObjectsFinder', () => { }); it('should not render tags filter button if savedObjectsTagging is undefined', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ - saved_objects: [doc, doc2, doc3], - }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2, doc3] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -767,22 +561,13 @@ describe('SavedObjectsFinder', () => { }); it('should apply types filter if selected', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ - saved_objects: [doc, doc2, doc3], - }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2, doc3] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -793,50 +578,36 @@ describe('SavedObjectsFinder', () => { const table = wrapper.find>(EuiInMemoryTable); const search = table.prop('search') as EuiSearchBarProps; search.onChange?.({ query: Query.parse('type:(vis)'), queryText: '', error: null }); - expect(core.http.get).toHaveBeenLastCalledWith('/internal/saved-objects-finder/find', { - query: { - type: ['vis'], - fields: ['title', 'name'], - search: undefined, - hasReference: undefined, - page: 1, - perPage: 10, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', - }, + expect(contentClient.mSearch).toHaveBeenLastCalledWith({ + contentTypes: [ + { + contentTypeId: 'vis', + }, + ], + query: { limit: 10, text: undefined }, }); search.onChange?.({ query: Query.parse('type:(search or vis)'), queryText: '', error: null }); - expect(core.http.get).toHaveBeenLastCalledWith('/internal/saved-objects-finder/find', { - query: { - type: ['search', 'vis'], - fields: ['title', 'name'], - search: undefined, - hasReference: undefined, - page: 1, - perPage: 10, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', - }, + expect(contentClient.mSearch).toHaveBeenLastCalledWith({ + contentTypes: [ + { + contentTypeId: 'search', + }, + { + contentTypeId: 'vis', + }, + ], + query: { limit: 10, text: undefined }, }); }); it('should apply tags filter if selected', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ - saved_objects: [doc, doc2, doc3], - }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [doc, doc2, doc3] }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -847,50 +618,53 @@ describe('SavedObjectsFinder', () => { const table = wrapper.find>(EuiInMemoryTable); const search = table.prop('search') as EuiSearchBarProps; search.onChange?.({ query: Query.parse('tag:(tag1)'), queryText: '', error: null }); - expect(core.http.get).toHaveBeenLastCalledWith('/internal/saved-objects-finder/find', { + expect(contentClient.mSearch).toHaveBeenLastCalledWith({ + contentTypes: [ + { + contentTypeId: 'search', + }, + { + contentTypeId: 'vis', + }, + ], query: { - type: ['search', 'vis'], - fields: ['title', 'name'], - search: undefined, - hasReference: JSON.stringify(['tag1']), - page: 1, - perPage: 10, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', + limit: 10, + text: undefined, + tags: { + included: ['tag1'], + }, }, }); search.onChange?.({ query: Query.parse('tag:(tag1 or tag2)'), queryText: '', error: null }); - expect(core.http.get).toHaveBeenLastCalledWith('/internal/saved-objects-finder/find', { + expect(contentClient.mSearch).toHaveBeenLastCalledWith({ + contentTypes: [ + { + contentTypeId: 'search', + }, + { + contentTypeId: 'vis', + }, + ], query: { - type: ['search', 'vis'], - fields: ['title', 'name'], - search: undefined, - hasReference: JSON.stringify(['tag1', 'tag2']), - page: 1, - perPage: 10, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', + limit: 10, + text: undefined, + tags: { + included: ['tag1', 'tag2'], + }, }, }); }); }); it('should display no items message if there are no items', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [] }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: [] }) ); - core.uiSettings.get.mockImplementation(() => 10); const noItemsMessage = ; const wrapper = mount( @@ -912,20 +686,13 @@ describe('SavedObjectsFinder', () => { })); it('should show a table pagination with initial per page', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: longItemList }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: longItemList }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -941,20 +708,13 @@ describe('SavedObjectsFinder', () => { }); it('should allow switching the page size', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: longItemList }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: longItemList }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -976,20 +736,13 @@ describe('SavedObjectsFinder', () => { }); it('should switch page correctly', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: longItemList }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: longItemList }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -1014,20 +767,13 @@ describe('SavedObjectsFinder', () => { }); it('should show an ordinary pagination for fixed page sizes', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: longItemList }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: longItemList }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -1043,20 +789,13 @@ describe('SavedObjectsFinder', () => { }); it('should switch page correctly for fixed page sizes', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: longItemList }) + (contentClient.mSearch as any as jest.SpyInstance).mockImplementation(() => + Promise.resolve({ hits: longItemList }) ); - core.uiSettings.get.mockImplementation(() => 10); const wrapper = mount( @@ -1083,18 +822,11 @@ describe('SavedObjectsFinder', () => { describe('loading state', () => { it('should display a loading indicator during initial loading', () => { - const core = coreMock.createStart(); - (core.http.get as jest.Mock).mockResolvedValue({ saved_objects: [] }); - core.uiSettings.get.mockImplementation(() => 10); + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ hits: [] }); const wrapper = mount( ); @@ -1103,20 +835,11 @@ describe('SavedObjectsFinder', () => { }); it('should hide the loading indicator if data is shown', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc] }) - ); - core.uiSettings.get.mockImplementation(() => 10); + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ hits: [doc] }); const wrapper = mount( { }); it('should show the loading indicator if there are already items and the search is updated', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc] }) - ); - core.uiSettings.get.mockImplementation(() => 10); + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ hits: [doc] }); const wrapper = mount( ); @@ -1165,20 +879,11 @@ describe('SavedObjectsFinder', () => { }); it('should render with children', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) - ); - core.uiSettings.get.mockImplementation(() => 10); + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ hits: [doc, doc2] }); const wrapper = mount( { describe('columns', () => { it('should show all columns', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2, doc3] }) - ); - core.uiSettings.get.mockImplementation(() => 10); + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ + hits: [doc, doc2, doc3], + }); const wrapper = mount( ); @@ -1228,20 +926,13 @@ describe('SavedObjectsFinder', () => { }); it('should hide the type column if there is only one type in the metadata list', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2] }) - ); - core.uiSettings.get.mockImplementation(() => 10); + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ + hits: [doc, doc2], + }); const wrapper = mount( ); @@ -1256,20 +947,13 @@ describe('SavedObjectsFinder', () => { }); it('should hide the tags column if savedObjectsTagging is undefined', async () => { - const core = coreMock.createStart(); - (core.http.get as any as jest.SpyInstance).mockImplementation(() => - Promise.resolve({ saved_objects: [doc, doc2, doc3] }) - ); - core.uiSettings.get.mockImplementation(() => 10); + (contentClient.mSearch as any as jest.SpyInstance).mockResolvedValue({ + hits: [doc, doc2, doc3], + }); const wrapper = mount( ); diff --git a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx index 3283bfed3fa64..aad1734dff657 100644 --- a/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx +++ b/src/plugins/saved_objects_finder/public/finder/saved_object_finder.tsx @@ -9,44 +9,39 @@ import { debounce } from 'lodash'; import PropTypes from 'prop-types'; import React, { ReactElement, ReactNode } from 'react'; -import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { getTagFindReferences, parseQuery } from '@kbn/saved-objects-management-plugin/public'; +import type { ContentClient } from '@kbn/content-management-plugin/public'; +import type { IUiSettingsClient } from '@kbn/core/public'; import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, EuiInMemoryTable, EuiLink, + EuiSearchBarProps, EuiTableFieldDataColumnType, - IconType, - EuiIcon, + EuiText, EuiToolTip, - EuiSearchBarProps, - SearchFilterConfig, - Query, + IconType, PropertySort, - EuiFlexItem, - EuiFlexGroup, - EuiText, + Query, + SearchFilterConfig, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { IUiSettingsClient, HttpStart } from '@kbn/core/public'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { - SavedObjectCommon, - FindQueryHTTP, - FindResponseHTTP, - FinderAttributes, - LISTING_LIMIT_SETTING, -} from '../../common'; - -export interface SavedObjectMetaData { +import { FinderAttributes, SavedObjectCommon, LISTING_LIMIT_SETTING } from '../../common'; + +export interface SavedObjectMetaData { type: string; name: string; getIconForSavedObject(savedObject: SavedObjectCommon): IconType; getTooltipForSavedObject?(savedObject: SavedObjectCommon): string; showSavedObject?(savedObject: SavedObjectCommon): boolean; getSavedObjectSubType?(savedObject: SavedObjectCommon): string; + /** @deprecated doesn't do anything, the full object is returned **/ includeFields?: string[]; - defaultSearchField?: string; } interface SavedObjectFinderItem extends SavedObjectCommon { @@ -63,10 +58,9 @@ interface SavedObjectFinderState { } interface SavedObjectFinderServices { - http: HttpStart; - uiSettings: IUiSettingsClient; - savedObjectsManagement: SavedObjectsManagementPluginStart; savedObjectsTagging?: SavedObjectsTaggingApi; + contentClient: ContentClient; + uiSettings: IUiSettingsClient; } interface BaseSavedObjectFinder { @@ -113,21 +107,9 @@ export class SavedObjectFinderUi extends React.Component< private debouncedFetch = debounce(async (query: Query) => { const metaDataMap = this.getSavedObjectMetaDataMap(); - const { savedObjectsManagement, uiSettings, http } = this.props.services; - - const fields = Object.values(metaDataMap) - .map((metaData) => metaData.includeFields || []) - .reduce((allFields, currentFields) => allFields.concat(currentFields), ['title', 'name']); + const { contentClient, uiSettings } = this.props.services; - const additionalSearchFields = Object.values(metaDataMap).reduce((col, item) => { - if (item.defaultSearchField) { - col.push(item.defaultSearchField); - } - return col; - }, []); - - const perPage = uiSettings.get(LISTING_LIMIT_SETTING); - const { queryText, visibleTypes, selectedTags } = savedObjectsManagement.parseQuery( + const { queryText, visibleTypes, selectedTags } = parseQuery( query, Object.values(metaDataMap).map((metadata) => ({ name: metadata.type, @@ -136,26 +118,23 @@ export class SavedObjectFinderUi extends React.Component< displayName: metadata.name, })) ); - const hasReference = savedObjectsManagement.getTagFindReferences({ + const includeTags = getTagFindReferences({ selectedTags, taggingApi: this.props.services.savedObjectsTagging, - }); - const params: FindQueryHTTP = { - type: visibleTypes ?? Object.keys(metaDataMap), - search: queryText ? `${queryText}*` : undefined, - fields: [...new Set(fields)], - page: 1, - perPage, - searchFields: ['title^3', 'description', ...additionalSearchFields], - defaultSearchOperator: 'AND', - hasReference: hasReference ? JSON.stringify(hasReference) : undefined, - }; + })?.map(({ id, type }) => id); - const response = (await http.get('/internal/saved-objects-finder/find', { - query: params as Record, - })) as FindResponseHTTP; + const types = visibleTypes ?? Object.keys(metaDataMap); + + const response = await contentClient.mSearch>({ + contentTypes: types.map((type) => ({ contentTypeId: type })), + query: { + text: queryText ? `${queryText}*` : undefined, + ...(includeTags?.length ? { tags: { included: includeTags } } : {}), + limit: uiSettings.get(LISTING_LIMIT_SETTING), // TODO: support pagination, + }, + }); - const savedObjects = response.saved_objects + const savedObjects = response.hits .map((savedObject) => { const { attributes: { name, title }, @@ -270,7 +249,7 @@ export class SavedObjectFinderUi extends React.Component< currentSavedObjectMetaData || ({ getIconForSavedObject: () => 'document', - } as Pick, 'getIconForSavedObject'>) + } as Pick) ).getIconForSavedObject(item.simple); return ( diff --git a/src/plugins/saved_objects_finder/server/plugin.test.ts b/src/plugins/saved_objects_finder/server/plugin.test.ts index 68569a6a24c17..3521acd5594b1 100644 --- a/src/plugins/saved_objects_finder/server/plugin.test.ts +++ b/src/plugins/saved_objects_finder/server/plugin.test.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import { registerRoutesMock } from './plugin.test.mocks'; - import { coreMock } from '@kbn/core/server/mocks'; import { SavedObjectsServerPlugin } from './plugin'; import { uiSettings } from './ui_settings'; @@ -21,17 +19,11 @@ describe('SavedObjectsPlugin', () => { plugin = new SavedObjectsServerPlugin(); }); - afterEach(() => { - registerRoutesMock.mockReset(); - }); - describe('#setup', () => { - it('calls `registerRoutes` and `registerSettings` with the correct parameters', () => { + it('calls `registerSettings` with the correct parameters', () => { plugin.setup(coreSetup); expect(coreSetup.uiSettings.register).toHaveBeenCalledWith(uiSettings); - expect(coreSetup.http.createRouter).toHaveBeenCalledTimes(1); - expect(registerRoutesMock).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/plugins/saved_objects_finder/server/plugin.ts b/src/plugins/saved_objects_finder/server/plugin.ts index 6d01010e37612..f088465053c57 100644 --- a/src/plugins/saved_objects_finder/server/plugin.ts +++ b/src/plugins/saved_objects_finder/server/plugin.ts @@ -6,15 +6,12 @@ * Side Public License, v 1. */ -import type { CoreSetup, Plugin, RequestHandlerContext } from '@kbn/core/server'; -import { registerRoutes } from './routes'; +import type { CoreSetup, Plugin } from '@kbn/core/server'; import { uiSettings } from './ui_settings'; export class SavedObjectsServerPlugin implements Plugin { public setup(core: CoreSetup) { core.uiSettings.register(uiSettings); - const router = core.http.createRouter(); - registerRoutes(router); return {}; } diff --git a/src/plugins/saved_objects_finder/server/routes/find.ts b/src/plugins/saved_objects_finder/server/routes/find.ts deleted file mode 100644 index ce9377ce25333..0000000000000 --- a/src/plugins/saved_objects_finder/server/routes/find.ts +++ /dev/null @@ -1,63 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { schema } from '@kbn/config-schema'; -import { SavedObjectsRouter } from '../types'; -import type { SavedObjectCommon, FindResponseHTTP } from '../../common'; - -export const registerFindRoute = (router: SavedObjectsRouter) => { - router.get( - { - path: '/internal/saved-objects-finder/find', - validate: { - query: schema.object({ - perPage: schema.number({ min: 0, defaultValue: 20 }), - page: schema.number({ min: 0, defaultValue: 1 }), - type: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), - search: schema.maybe(schema.string()), - defaultSearchOperator: schema.oneOf([schema.literal('AND'), schema.literal('OR')]), - sortField: schema.maybe(schema.string()), - sortOrder: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), - fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], { - defaultValue: [], - }), - searchFields: schema.maybe(schema.arrayOf(schema.string())), - hasReference: schema.maybe(schema.string()), - }), - }, - options: { - authRequired: 'optional', - }, - }, - async (ctx, req, res) => { - const savedObjectsClient = (await ctx.core).savedObjects.client; - const { query } = req; - - const searchTypes = Array.isArray(query.type) ? query.type : [query.type]; - const includedFields = Array.isArray(query.fields) ? query.fields : [query.fields]; - - const findResponse = await savedObjectsClient.find>({ - ...query, - type: searchTypes, - fields: includedFields, - hasReference: query.hasReference ? JSON.parse(query.hasReference) : undefined, - }); - - const savedObjects = findResponse.saved_objects; - - const response: FindResponseHTTP = { - saved_objects: savedObjects, - total: findResponse.total, - per_page: findResponse.per_page, - page: findResponse.page, - }; - - return res.ok({ body: response }); - } - ); -}; diff --git a/src/plugins/saved_objects_finder/tsconfig.json b/src/plugins/saved_objects_finder/tsconfig.json index 217f4f7f0dbe3..cecc9fbdadb61 100644 --- a/src/plugins/saved_objects_finder/tsconfig.json +++ b/src/plugins/saved_objects_finder/tsconfig.json @@ -10,11 +10,11 @@ "@kbn/test-jest-helpers", "@kbn/saved-objects-tagging-oss-plugin", "@kbn/i18n", - "@kbn/core-saved-objects-server", "@kbn/config-schema", - "@kbn/core-ui-settings-browser", - "@kbn/core-http-browser", "@kbn/saved-objects-settings", + "@kbn/content-management-plugin", + "@kbn/content-management-utils", + "@kbn/core-ui-settings-browser", ], "exclude": [ "target/**/*", diff --git a/src/plugins/saved_objects_management/public/index.ts b/src/plugins/saved_objects_management/public/index.ts index 223a6333a0db7..6df2288d110b5 100644 --- a/src/plugins/saved_objects_management/public/index.ts +++ b/src/plugins/saved_objects_management/public/index.ts @@ -23,7 +23,7 @@ export type { } from './services'; export { SavedObjectsManagementAction } from './services'; export type { ProcessedImportResponse, FailedImport } from './lib'; -export { processImportResponse } from './lib'; +export { processImportResponse, getTagFindReferences, parseQuery } from './lib'; export type { SavedObjectRelation, SavedObjectWithMetadata, diff --git a/src/plugins/share/public/plugin.test.ts b/src/plugins/share/public/plugin.test.ts index ece207a67aa40..878c285b3a1a1 100644 --- a/src/plugins/share/public/plugin.test.ts +++ b/src/plugins/share/public/plugin.test.ts @@ -54,6 +54,7 @@ describe('SharePlugin', () => { expect.objectContaining({ getShareMenuItems: expect.any(Function), }), + true, // disableEmbed - true because buildFlavor === 'serverless' undefined ); expect(start.toggleShareContextMenu).toBeDefined(); @@ -73,6 +74,7 @@ describe('SharePlugin', () => { expect.objectContaining({ getShareMenuItems: expect.any(Function), }), + true, // disableEmbed - true because buildFlavor === 'serverless' anonymousAccessServiceProvider ); expect(start.toggleShareContextMenu).toBeDefined(); diff --git a/src/plugins/share/public/plugin.ts b/src/plugins/share/public/plugin.ts index 792e0dae0b986..11aa38600d98b 100644 --- a/src/plugins/share/public/plugin.ts +++ b/src/plugins/share/public/plugin.ts @@ -120,10 +120,12 @@ export class SharePlugin implements Plugin { } public start(core: CoreStart): SharePluginStart { + const disableEmbed = this.initializerContext.env.packageInfo.buildFlavor === 'serverless'; const sharingContextMenuStart = this.shareContextMenu.start( core, this.url!, this.shareMenuRegistry.start(), + disableEmbed, this.anonymousAccessServiceProvider ); diff --git a/src/plugins/share/public/services/share_menu_manager.tsx b/src/plugins/share/public/services/share_menu_manager.tsx index 45720157d93df..658d01063b1ea 100644 --- a/src/plugins/share/public/services/share_menu_manager.tsx +++ b/src/plugins/share/public/services/share_menu_manager.tsx @@ -28,6 +28,7 @@ export class ShareMenuManager { core: CoreStart, urlService: BrowserUrlService, shareRegistry: ShareMenuRegistryStart, + disableEmbed: boolean, anonymousAccessServiceProvider?: () => AnonymousAccessServiceContract ) { return { @@ -45,6 +46,7 @@ export class ShareMenuManager { const anonymousAccess = anonymousAccessServiceProvider?.(); this.toggleShareContextMenu({ ...options, + allowEmbed: disableEmbed ? false : options.allowEmbed, onClose, menuItems, urlService, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 5a79e7cb384e3..52a54eb0335b1 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -4849,6 +4849,137 @@ } } }, + "observabilityAIAssistant": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "Always `main`" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 90 days" + } + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "The application view being tracked" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application sub view since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application sub view is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 90 days" + } + } + } + } + } + } + }, "exploratory-view": { "properties": { "appId": { diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index 98ad307ff1f97..e8ee3f39996a3 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import type { AggregateQuery } from '@kbn/es-query'; import type { DataViewField, DataViewSpec, DataView } from '@kbn/data-views-plugin/public'; import { ActionInternal } from './actions/action_internal'; @@ -19,6 +19,7 @@ export interface VisualizeFieldContext { fieldName: string; dataViewSpec: DataViewSpec; contextualFields?: string[]; + textBasedColumns?: DatatableColumn[]; originatingApp?: string; query?: AggregateQuery; } diff --git a/src/plugins/ui_actions/tsconfig.json b/src/plugins/ui_actions/tsconfig.json index 7763e82682833..bac1778817bb2 100644 --- a/src/plugins/ui_actions/tsconfig.json +++ b/src/plugins/ui_actions/tsconfig.json @@ -13,6 +13,7 @@ "@kbn/i18n", "@kbn/es-query", "@kbn/ui-actions-browser", + "@kbn/expressions-plugin", ], "exclude": [ "target/**/*", diff --git a/src/plugins/unified_histogram/public/chart/histogram.test.tsx b/src/plugins/unified_histogram/public/chart/histogram.test.tsx index e935e3a05aa87..4c651f0b5e391 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.test.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.test.tsx @@ -207,6 +207,7 @@ describe('Histogram', () => { const embeddable = unifiedHistogramServicesMock.lens.EmbeddableComponent; const onLoad = component.find(embeddable).props().onLoad; const adapters = createDefaultInspectorAdapters(); + adapters.tables.tables.unifiedHistogram = { meta: { statistics: { totalCount: 100 } } } as any; const rawResponse = { _shards: { total: 1, @@ -215,14 +216,21 @@ describe('Histogram', () => { failed: 1, failures: [], }, + hits: { + total: 100, + max_score: null, + hits: [], + }, }; jest .spyOn(adapters.requests, 'getRequests') .mockReturnValue([{ response: { json: { rawResponse } } } as any]); - onLoad(false, adapters); + act(() => { + onLoad(false, adapters); + }); expect(props.onTotalHitsChange).toHaveBeenLastCalledWith( - UnifiedHistogramFetchStatus.error, - undefined + UnifiedHistogramFetchStatus.complete, + 100 ); expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); }); diff --git a/src/plugins/unified_histogram/public/chart/histogram.tsx b/src/plugins/unified_histogram/public/chart/histogram.tsx index 9983f2e0841dd..761e701e8f9a6 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.tsx @@ -101,10 +101,10 @@ export function Histogram({ | undefined; const response = json?.rawResponse; - // Lens will swallow shard failures and return `isLoading: false` because it displays - // its own errors, but this causes us to emit onTotalHitsChange(UnifiedHistogramFetchStatus.complete, 0). - // This is incorrect, so we check for request failures and shard failures here, and emit an error instead. - if (requestFailed || response?._shards.failed) { + // The response can have `response?._shards.failed` but we should still be able to show hits number + // TODO: show shards warnings as a badge next to the total hits number + + if (requestFailed) { onTotalHitsChange?.(UnifiedHistogramFetchStatus.error, undefined); onChartLoad?.({ adapters: adapters ?? {} }); return; diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts b/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts index 6903cdf6b4256..c260d3171697b 100644 --- a/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts +++ b/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts @@ -206,6 +206,7 @@ const fetchTotalHitsSearchSource = async ({ executionContext: { description: 'fetch total hits', }, + disableShardFailureWarning: true, // TODO: show warnings as a badge next to total hits number }) .pipe( filter((res) => isCompleteResponse(res)), diff --git a/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts b/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts index fffadcc9bc619..e4936b9345a6d 100644 --- a/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts +++ b/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts @@ -8,6 +8,7 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { AggregateQuery, isOfAggregateQueryType, Query } from '@kbn/es-query'; +import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { LensSuggestionsApi, Suggestion } from '@kbn/lens-plugin/public'; import { isEqual } from 'lodash'; import { useEffect, useMemo, useRef, useState } from 'react'; @@ -25,7 +26,7 @@ export const useLensSuggestions = ({ query?: Query | AggregateQuery; originalSuggestion?: Suggestion; isPlainRecord?: boolean; - columns?: string[]; + columns?: DatatableColumn[]; lensSuggestionsApi: LensSuggestionsApi; onSuggestionChange?: (suggestion: Suggestion | undefined) => void; }) => { @@ -33,16 +34,17 @@ export const useLensSuggestions = ({ const context = { dataViewSpec: dataView?.toSpec(), fieldName: '', - contextualFields: columns, + textBasedColumns: columns, query: query && isOfAggregateQueryType(query) ? query : undefined, }; const allSuggestions = isPlainRecord ? lensSuggestionsApi(context, dataView, ['lnsDatatable']) ?? [] : []; + const [firstSuggestion] = allSuggestions; return { firstSuggestion, allSuggestions }; - }, [columns, dataView, isPlainRecord, lensSuggestionsApi, query]); + }, [dataView, isPlainRecord, lensSuggestionsApi, query, columns]); const [allSuggestions, setAllSuggestions] = useState(suggestions.allSuggestions); const currentSuggestion = originalSuggestion ?? suggestions.firstSuggestion; @@ -80,5 +82,5 @@ const getSuggestionDeps = ({ }: { dataView: DataView; query?: Query | AggregateQuery; - columns?: string[]; + columns?: DatatableColumn[]; }) => [dataView.id, columns, query]; diff --git a/src/plugins/unified_histogram/public/layout/layout.tsx b/src/plugins/unified_histogram/public/layout/layout.tsx index cd6d2ffb5ec07..4d0605c4f72b3 100644 --- a/src/plugins/unified_histogram/public/layout/layout.tsx +++ b/src/plugins/unified_histogram/public/layout/layout.tsx @@ -11,7 +11,7 @@ import { PropsWithChildren, ReactElement, RefObject } from 'react'; import React, { useMemo } from 'react'; import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { css } from '@emotion/css'; -import type { Datatable } from '@kbn/expressions-plugin/common'; +import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import type { LensEmbeddableInput, LensSuggestionsApi, Suggestion } from '@kbn/lens-plugin/public'; import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; @@ -69,7 +69,7 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren /** * The current columns */ - columns?: string[]; + columns?: DatatableColumn[]; /** * Context object for requests made by Unified Histogram components -- optional */ diff --git a/src/plugins/vis_types/table/public/components/table_vis_columns.tsx b/src/plugins/vis_types/table/public/components/table_vis_columns.tsx index 4f3be134ad939..e4eed702cb908 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_columns.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_columns.tsx @@ -69,13 +69,13 @@ export const createGridColumns = ( const filterForText = i18n.translate( 'visTypeTable.tableCellFilter.filterForValueText', { - defaultMessage: 'Filter for value', + defaultMessage: 'Filter for', } ); const filterForAriaLabel = i18n.translate( 'visTypeTable.tableCellFilter.filterForValueAriaLabel', { - defaultMessage: 'Filter for value: {cellContent}', + defaultMessage: 'Filter for: {cellContent}', values: { cellContent, }, @@ -105,13 +105,13 @@ export const createGridColumns = ( const filterOutText = i18n.translate( 'visTypeTable.tableCellFilter.filterOutValueText', { - defaultMessage: 'Filter out value', + defaultMessage: 'Filter out', } ); const filterOutAriaLabel = i18n.translate( 'visTypeTable.tableCellFilter.filterOutValueAriaLabel', { - defaultMessage: 'Filter out value: {cellContent}', + defaultMessage: 'Filter out: {cellContent}', values: { cellContent, }, diff --git a/src/plugins/vis_types/vislib/public/vislib/components/legend/legend_item.tsx b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend_item.tsx index c137a689aafba..663e5ea368a32 100644 --- a/src/plugins/vis_types/vislib/public/vislib/components/legend/legend_item.tsx +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend_item.tsx @@ -65,7 +65,7 @@ const VisLegendItemComponent = ({ { id: 'filterIn', label: i18n.translate('visTypeVislib.vislib.legend.filterForValueButtonAriaLabel', { - defaultMessage: 'Filter for value {legendDataLabel}', + defaultMessage: 'Filter for {legendDataLabel}', values: { legendDataLabel: item.label }, }), iconType: 'plusInCircle', @@ -74,7 +74,7 @@ const VisLegendItemComponent = ({ { id: 'filterOut', label: i18n.translate('visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel', { - defaultMessage: 'Filter out value {legendDataLabel}', + defaultMessage: 'Filter out {legendDataLabel}', values: { legendDataLabel: item.label }, }), iconType: 'minusInCircle', diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index da71f2fd59854..c04dec48f0c97 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -57,6 +57,7 @@ import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_obje import type { VisualizationsStartDeps } from '../plugin'; interface VisualizationAttributes extends SavedObjectAttributes { + title: string; visState: string; } diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx index a150a94a60516..907c989c7bb43 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx @@ -12,8 +12,7 @@ import { TypesStart, VisGroups, BaseVisType } from '../vis_types'; import NewVisModal from './new_vis_modal'; import { ApplicationStart, DocLinksStart } from '@kbn/core/public'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import { httpServiceMock } from '@kbn/core-http-browser-mocks'; -import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; describe('NewVisModal', () => { const defaultVisTypeParams = { @@ -78,8 +77,8 @@ describe('NewVisModal', () => { }, }, }; - const http = httpServiceMock.createStartContract({ basePath: '' }); - const savedObjectsManagement = savedObjectsManagementPluginMock.createStartContract(); + + const contentManagement = contentManagementMock.createStartContract(); beforeAll(() => { Object.defineProperty(window, 'location', { @@ -103,8 +102,7 @@ describe('NewVisModal', () => { uiSettings={uiSettings} application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(true); @@ -121,8 +119,7 @@ describe('NewVisModal', () => { uiSettings={uiSettings} application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(true); @@ -138,8 +135,7 @@ describe('NewVisModal', () => { uiSettings={uiSettings} application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); expect(wrapper.find('[data-test-subj="visType-vis2"]').exists()).toBe(true); @@ -156,8 +152,7 @@ describe('NewVisModal', () => { uiSettings={uiSettings} application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); const visCard = wrapper.find('[data-test-subj="visType-vis"]').last(); @@ -176,8 +171,7 @@ describe('NewVisModal', () => { uiSettings={uiSettings} application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); const visCard = wrapper.find('[data-test-subj="visType-vis"]').last(); @@ -203,8 +197,7 @@ describe('NewVisModal', () => { application={{ navigateToApp } as unknown as ApplicationStart} docLinks={docLinks as DocLinksStart} stateTransfer={stateTransfer} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').last(); @@ -229,8 +222,7 @@ describe('NewVisModal', () => { uiSettings={uiSettings} application={{ navigateToApp } as unknown as ApplicationStart} docLinks={docLinks as DocLinksStart} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').last(); @@ -251,8 +243,7 @@ describe('NewVisModal', () => { uiSettings={uiSettings} application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} - http={http} - savedObjectsManagement={savedObjectsManagement} + contentClient={contentManagement.client} /> ); const aggBasedGroupCard = wrapper diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index 4ff2138169083..ac1f89e73700f 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -12,9 +12,9 @@ import { EuiModal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import { ApplicationStart, IUiSettingsClient, DocLinksStart, HttpStart } from '@kbn/core/public'; +import { ApplicationStart, DocLinksStart, IUiSettingsClient } from '@kbn/core/public'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentClient } from '@kbn/content-management-plugin/public'; import { SearchSelection } from './search_selection'; import { GroupSelection } from './group_selection'; import { AggBasedSelection } from './agg_based_selection'; @@ -22,6 +22,7 @@ import type { TypesStart, BaseVisType, VisTypeAlias } from '../vis_types'; import './dialog.scss'; interface TypeSelectionProps { + contentClient: ContentClient; isOpen: boolean; onClose: () => void; visTypesRegistry: TypesStart; @@ -29,14 +30,12 @@ interface TypeSelectionProps { addBasePath: (path: string) => string; uiSettings: IUiSettingsClient; docLinks: DocLinksStart; - http: HttpStart; application: ApplicationStart; outsideVisualizeApp?: boolean; stateTransfer?: EmbeddableStateTransfer; originatingApp?: string; showAggsSelection?: boolean; selectedVisType?: BaseVisType; - savedObjectsManagement: SavedObjectsManagementPluginStart; } interface TypeSelectionState { @@ -88,11 +87,10 @@ class NewVisModal extends React.Component this.setState({ showSearchVisModal: false })} /> diff --git a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx index 341123b780445..374d2c4b8fa39 100644 --- a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx +++ b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx @@ -10,20 +10,18 @@ import React from 'react'; import { EuiModalBody, EuiModalHeader, EuiModalHeaderTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { IUiSettingsClient, HttpStart } from '@kbn/core/public'; - -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentClient } from '@kbn/content-management-plugin/public'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; +import { IUiSettingsClient } from '@kbn/core/public'; import type { BaseVisType } from '../../vis_types'; import { DialogNavigation } from '../dialog_navigation'; import { showSavedObject } from './show_saved_object'; interface SearchSelectionProps { + contentClient: ContentClient; + uiSettings: IUiSettingsClient; onSearchSelected: (searchId: string, searchType: string) => void; visType: BaseVisType; - uiSettings: IUiSettingsClient; - http: HttpStart; - savedObjectsManagement: SavedObjectsManagementPluginStart; goBack: () => void; } @@ -81,14 +79,12 @@ export class SearchSelection extends React.Component { defaultMessage: 'Data view', } ), - defaultSearchField: 'name', }, ]} fixedPageSize={this.fixedPageSize} services={{ + contentClient: this.props.contentClient, uiSettings: this.props.uiSettings, - http: this.props.http, - savedObjectsManagement: this.props.savedObjectsManagement, }} /> diff --git a/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts b/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts index 02085d5acc2ba..3e61b0363b2bc 100644 --- a/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts +++ b/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts @@ -8,12 +8,12 @@ import type { SavedObjectCommon, FinderAttributes } from '@kbn/saved-objects-finder-plugin/common'; -export interface SavedSearchesAttributes extends SavedObjectCommon { +export interface SavedSearchesAttributes extends FinderAttributes { isTextBasedQuery: boolean; usesAdHocDataView?: boolean; } -export const showSavedObject = (savedObject: SavedObjectCommon) => { - const so = savedObject as unknown as SavedObjectCommon; +export const showSavedObject = (savedObject: SavedObjectCommon) => { + const so = savedObject as SavedObjectCommon; return !so.attributes.isTextBasedQuery && !so.attributes.usesAdHocDataView; }; diff --git a/src/plugins/visualizations/public/wizard/show_new_vis.tsx b/src/plugins/visualizations/public/wizard/show_new_vis.tsx index 3252a92ae8a4c..b4bfe17b177c4 100644 --- a/src/plugins/visualizations/public/wizard/show_new_vis.tsx +++ b/src/plugins/visualizations/public/wizard/show_new_vis.tsx @@ -14,12 +14,12 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { getHttp, getTypes, - getUISettings, getApplication, getEmbeddable, getDocLinks, getTheme, - getSavedObjectsManagement, + getContentManagement, + getUISettings, } from '../services'; import type { BaseVisType } from '../vis_types'; @@ -79,10 +79,9 @@ export function showNewVisModal({ outsideVisualizeApp={outsideVisualizeApp} editorParams={editorParams} visTypesRegistry={getTypes()} - addBasePath={getHttp().basePath.prepend} + contentClient={getContentManagement().client} uiSettings={getUISettings()} - http={getHttp()} - savedObjectsManagement={getSavedObjectsManagement()} + addBasePath={getHttp().basePath.prepend} application={getApplication()} docLinks={getDocLinks()} showAggsSelection={showAggsSelection} diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index 4df8ede52adcb..e4c302c17a43b 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -50,7 +50,6 @@ "@kbn/core-overlays-browser", "@kbn/config-schema", "@kbn/usage-collection-plugin", - "@kbn/core-http-browser-mocks", "@kbn/shared-ux-router", "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-finder-plugin", diff --git a/src/setup_node_env/dns_ipv4_first.js b/src/setup_node_env/dns_ipv4_first.js new file mode 100644 index 0000000000000..75dd876c46d35 --- /dev/null +++ b/src/setup_node_env/dns_ipv4_first.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. + */ + +// enables Node 16 default DNS lookup behavior for the current thread +require('dns').setDefaultResultOrder('ipv4first'); + +// overrides current process node options, so it can be restored in worker threads too +process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ''} --dns-result-order=ipv4first`; diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index 4f7babd66ac58..176785e10246a 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -10,7 +10,7 @@ require('./setup_env'); // restore < Node 16 default DNS lookup behavior -require('dns').setDefaultResultOrder('ipv4first'); +require('./dns_ipv4_first'); require('@kbn/babel-register').install(); require('./polyfill'); diff --git a/test/examples/content_management/finder.ts b/test/examples/content_management/finder.ts new file mode 100644 index 0000000000000..76c277ef75a1c --- /dev/null +++ b/test/examples/content_management/finder.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 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 expect from '@kbn/expect'; +import { PluginFunctionalProviderContext } from '../../plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const PageObjects = getPageObjects(['common', 'home', 'header']); + const log = getService('log'); + const testSubjects = getService('testSubjects'); + + describe('Finder demo', () => { + before(async () => { + await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { + useActualUrl: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.addSampleDataSet('flights'); + }); + after(async () => { + await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { + useActualUrl: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.removeSampleDataSet('flights'); + }); + + it('Finder demo works', async () => { + const appId = 'contentManagementExamples'; + await PageObjects.common.navigateToApp(appId, { + path: 'finder', + }); + + await testSubjects.existOrFail(`savedObjectsFinderTable`); + await testSubjects.existOrFail(`savedObjectFinderTitle`); + + const titles: string[] = []; + const titlesElements = await testSubjects.findAll(`savedObjectFinderTitle`); + for (let i = 0; i < titlesElements.length; i++) { + titles.push(await (await titlesElements[i].findByClassName(`euiLink`)).getVisibleText()); + } + + const expectExists = [ + `Kibana Sample Data Flights`, + `[Flights] Airport Connections (Hover Over Airport)`, + `[Flights] Departures Count Map`, + `[Flights] Origin Time Delayed`, + `[Flights] Flight Log`, + ]; + + expectExists.forEach((item) => { + log.debug(`Checking for ${item}`); + expect(titles.includes(item)).to.be(true); + }); + }); + }); +} diff --git a/test/examples/content_management/index.ts b/test/examples/content_management/index.ts index c02b022781187..15d71ecd2661e 100644 --- a/test/examples/content_management/index.ts +++ b/test/examples/content_management/index.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: PluginFunctionalProviderContext) { describe('content management examples', function () { loadTestFile(require.resolve('./todo_app')); loadTestFile(require.resolve('./msearch')); + loadTestFile(require.resolve('./finder')); }); } diff --git a/test/examples/content_management/msearch.ts b/test/examples/content_management/msearch.ts index 0a583b455cd04..52bb79dfd95fb 100644 --- a/test/examples/content_management/msearch.ts +++ b/test/examples/content_management/msearch.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ +import expect from '@kbn/expect'; import { PluginFunctionalProviderContext } from '../../plugin_functional/services'; // eslint-disable-next-line import/no-default-export export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { - const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common', 'home', 'header']); const listingTable = getService('listingTable'); @@ -37,11 +37,19 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide }); await listingTable.waitUntilTableIsLoaded(); - await listingTable.searchForItemWithName('Origin Time Delayed'); + const items = await listingTable.getAllItemsNames(); + const expectExists = [ + `kibana_sample_data_flights`, + `[Flights] Airport Connections (Hover Over Airport)`, + `[Flights] Departures Count Map`, + `[Flights] Global Flight Dashboard`, + `[Flights] Origin Time Delayed`, + `[Flights] Flight Log`, + ]; - await testSubjects.existOrFail( - `cm-msearch-tableListingTitleLink-[Flights]-Origin-Time-Delayed` - ); + expectExists.forEach((item) => { + expect(items.includes(item)).to.be(true); + }); }); }); } diff --git a/test/functional/apps/dashboard/group5/embed_mode.ts b/test/functional/apps/dashboard/group5/embed_mode.ts index 2599326daba43..16934fc9101a8 100644 --- a/test/functional/apps/dashboard/group5/embed_mode.ts +++ b/test/functional/apps/dashboard/group5/embed_mode.ts @@ -24,8 +24,7 @@ export default function ({ const screenshot = getService('screenshots'); const log = getService('log'); - // Failing: See https://github.com/elastic/kibana/issues/163207 - describe.skip('embed mode', () => { + describe('embed mode', () => { /* * Note: The baseline images used in all of the screenshot tests in this test suite were taken directly from the CI environment * in order to overcome a known issue with the pixel density of fonts being significantly different when running locally versus diff --git a/test/functional/apps/visualize/group5/_tsvb_time_series.ts b/test/functional/apps/visualize/group5/_tsvb_time_series.ts index 823276f0b21c8..eec30c52018a7 100644 --- a/test/functional/apps/visualize/group5/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/group5/_tsvb_time_series.ts @@ -166,7 +166,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('Clicking on the chart', () => { + describe('Clicking on the chart', function () { + this.tags('skipFirefox'); const act = async (visName: string, clickCoordinates: { x: number; y: number }) => { await testSubjects.click('visualizeSaveButton'); diff --git a/test/functional/apps/visualize/group6/_tsvb_tsdb_basic.ts b/test/functional/apps/visualize/group6/_tsvb_tsdb_basic.ts index 765c7a9d3ec4d..44953ae2e6619 100644 --- a/test/functional/apps/visualize/group6/_tsvb_tsdb_basic.ts +++ b/test/functional/apps/visualize/group6/_tsvb_tsdb_basic.ts @@ -61,15 +61,5 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(isFieldForAggregationValid).to.be(true); expect(await testSubjects.exists('visualization-error-text')).to.be(false); }); - - it('should show an error when using an unsupported tsdb field type', async () => { - await visualBuilder.selectAggType('Average'); - await visualBuilder.setFieldForAggregation('bytes_counter'); - // this is still returning true - const isFieldForAggregationValid = await visualBuilder.checkFieldForAggregationValidity(); - expect(isFieldForAggregationValid).to.be(true); - // but an error should appear in visualization - expect(await testSubjects.exists('visualization-error-text')).to.be(true); - }); }); } diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 67a8dd42b9ec1..4c5939f728f01 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -7,7 +7,6 @@ */ import type { DebugState } from '@elastic/charts'; -import expect from '@kbn/expect'; import { FtrService } from '../ftr_provider_context'; import { WebElementWrapper } from '../services/lib/web_element_wrapper'; @@ -845,10 +844,14 @@ export class VisualBuilderPageObject extends FtrService { ) { await this.setMetricsGroupBy('terms'); await this.common.sleep(1000); - const byField = await this.testSubjects.find('groupByField'); - await this.comboBox.setElement(byField, field); - const isSelected = await this.comboBox.isOptionSelected(byField, field); - expect(isSelected).to.be(true); + await this.retry.try(async () => { + const byField = await this.testSubjects.find('groupByField'); + await this.comboBox.setElement(byField, field); + const isSelected = await this.comboBox.isOptionSelected(byField, field); + if (!isSelected) { + throw new Error(`setMetricsGroupByTerms: failed to set '${field}' field`); + } + }); await this.setMetricsGroupByFiltering(filtering.include, filtering.exclude); } @@ -860,13 +863,17 @@ export class VisualBuilderPageObject extends FtrService { // In case of StaleElementReferenceError 'browser' service will try to find element again await fieldSelectAddButtonLast.click(); await this.common.sleep(2000); - const selectedByField = await this.find.byXPath( - `(//*[@data-test-subj='fieldSelectItem'])[last()]` - ); - await this.comboBox.setElement(selectedByField, field); - const isSelected = await this.comboBox.isOptionSelected(selectedByField, field); - expect(isSelected).to.be(true); + await this.retry.try(async () => { + const selectedByField = await this.find.byXPath( + `(//*[@data-test-subj='fieldSelectItem'])[last()]` + ); + await this.comboBox.setElement(selectedByField, field); + const isSelected = await this.comboBox.isOptionSelected(selectedByField, field); + if (!isSelected) { + throw new Error(`setAnotherGroupByTermsField: failed to set '${field}' field`); + } + }); } public async setMetricsGroupByFiltering(include?: string, exclude?: string) { diff --git a/test/functional/screenshots/baseline/dashboard_embed_mode.png b/test/functional/screenshots/baseline/dashboard_embed_mode.png index 0c6b375233679..592cd6c4ff403 100644 Binary files a/test/functional/screenshots/baseline/dashboard_embed_mode.png and b/test/functional/screenshots/baseline/dashboard_embed_mode.png differ diff --git a/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png b/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png index 822bcb90ae935..eed23bc7ed78f 100644 Binary files a/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png and b/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png differ diff --git a/test/functional/screenshots/baseline/dashboard_embed_mode_with_url_params.png b/test/functional/screenshots/baseline/dashboard_embed_mode_with_url_params.png index cbf96cdbf278b..26a18ede2d686 100644 Binary files a/test/functional/screenshots/baseline/dashboard_embed_mode_with_url_params.png and b/test/functional/screenshots/baseline/dashboard_embed_mode_with_url_params.png differ diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 673c092a546a9..66a2e385d3e6c 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -197,6 +197,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.cases.files.allowedMimeTypes (array)', 'xpack.cases.files.maxSize (number)', 'xpack.cases.markdownPlugins.lens (boolean)', + 'xpack.cases.stack.enabled (boolean)', 'xpack.ccr.ui.enabled (boolean)', 'xpack.cloud.base_url (string)', 'xpack.cloud.cname (string)', @@ -238,7 +239,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.graph.savePolicy (alternatives)', 'xpack.ilm.ui.enabled (boolean)', 'xpack.index_management.ui.enabled (boolean)', - 'xpack.index_management.enableIndexActions (boolean)', + 'xpack.index_management.enableIndexActions (any)', 'xpack.infra.sources.default.fields.message (array)', /** * xpack.infra.logs is conditional and will resolve to an object of properties diff --git a/test/scripts/jenkins_security_solution_cypress_firefox.sh b/test/scripts/jenkins_security_solution_cypress_firefox.sh deleted file mode 100755 index 79623d5a2a23b..0000000000000 --- a/test/scripts/jenkins_security_solution_cypress_firefox.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -source test/scripts/jenkins_test_setup_xpack.sh - -echo " -> Running security solution cypress tests" -cd "$XPACK_DIR" - -node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_INSTALL_DIR" \ - --config test/security_solution_cypress/config.firefox.ts - -echo "" -echo "" diff --git a/tsconfig.base.json b/tsconfig.base.json index 12504320663e3..8efe5c62421de 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1172,6 +1172,8 @@ "@kbn/screenshotting-plugin/*": ["x-pack/plugins/screenshotting/*"], "@kbn/search-examples-plugin": ["examples/search_examples"], "@kbn/search-examples-plugin/*": ["examples/search_examples/*"], + "@kbn/search-response-warnings": ["packages/kbn-search-response-warnings"], + "@kbn/search-response-warnings/*": ["packages/kbn-search-response-warnings/*"], "@kbn/searchprofiler-plugin": ["x-pack/plugins/searchprofiler"], "@kbn/searchprofiler-plugin/*": ["x-pack/plugins/searchprofiler/*"], "@kbn/security-api-integration-helpers": ["x-pack/test/security_api_integration/packages/helpers"], diff --git a/typings/index.d.ts b/typings/index.d.ts index d561c1444a77a..0134f5be84018 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -20,3 +20,5 @@ declare module 'react-syntax-highlighter/dist/cjs/prism-light'; declare module 'monaco-editor/esm/vs/basic-languages/markdown/markdown'; declare module 'monaco-editor/esm/vs/basic-languages/css/css'; declare module 'monaco-editor/esm/vs/basic-languages/yaml/yaml'; + +declare module 'find-cypress-specs'; diff --git a/x-pack/packages/ml/aiops_components/index.ts b/x-pack/packages/ml/aiops_components/index.ts index bfe84e260decd..c9ca50ad0daff 100644 --- a/x-pack/packages/ml/aiops_components/index.ts +++ b/x-pack/packages/ml/aiops_components/index.ts @@ -7,5 +7,9 @@ export { DualBrush, DualBrushAnnotation } from './src/dual_brush'; export { ProgressControls } from './src/progress_controls'; -export { DocumentCountChart } from './src/document_count_chart'; -export type { DocumentCountChartPoint, DocumentCountChartProps } from './src/document_count_chart'; +export { + DocumentCountChart, + type BrushSettings, + type BrushSelectionUpdateHandler, +} from './src/document_count_chart'; +export type { DocumentCountChartProps } from './src/document_count_chart'; diff --git a/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx b/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx index f20c5311d183a..967821db0e0db 100644 --- a/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx +++ b/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx @@ -27,14 +27,21 @@ import { import { i18n } from '@kbn/i18n'; import { IUiSettingsClient } from '@kbn/core/public'; -import { getSnappedWindowParameters, getWindowParameters } from '@kbn/aiops-utils'; -import type { WindowParameters } from '@kbn/aiops-utils'; +import { + getLogRateAnalysisType, + getSnappedWindowParameters, + getWindowParameters, + type LogRateAnalysisType, + type LogRateHistogramItem, + type WindowParameters, +} from '@kbn/aiops-utils'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; - import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; + import { DualBrush, DualBrushAnnotation } from '../..'; + import { BrushBadge } from './brush_badge'; declare global { @@ -51,20 +58,6 @@ interface TimeFilterRange { to: number; } -/** - * Datum for the bar chart - */ -export interface DocumentCountChartPoint { - /** - * Time of bucket - */ - time: number | string; - /** - * Number of doc count for that time bucket - */ - value: number; -} - /** * Brush settings */ @@ -83,6 +76,19 @@ export interface BrushSettings { badgeWidth?: number; } +/** + * Callback function which gets called when the brush selection has changed + * + * @param windowParameters Baseline and deviation time ranges. + * @param force Force update + * @param logRateAnalysisType `spike` or `dip` based on median log rate bucket size + */ +export type BrushSelectionUpdateHandler = ( + windowParameters: WindowParameters, + force: boolean, + logRateAnalysisType: LogRateAnalysisType +) => void; + /** * Props for document count chart */ @@ -94,14 +100,14 @@ export interface DocumentCountChartProps { fieldFormats: FieldFormatsStart; uiSettings: IUiSettingsClient; }; - /** Optional callback function which gets called the brush selection has changed */ - brushSelectionUpdateHandler?: (windowParameters: WindowParameters, force: boolean) => void; + /** Optional callback for handling brush selection updates */ + brushSelectionUpdateHandler?: BrushSelectionUpdateHandler; /** Optional width */ width?: number; /** Data chart points */ - chartPoints: DocumentCountChartPoint[]; + chartPoints: LogRateHistogramItem[]; /** Data chart points split */ - chartPointsSplit?: DocumentCountChartPoint[]; + chartPointsSplit?: LogRateHistogramItem[]; /** Start time range for the chart */ timeRangeEarliest: number; /** Ending time range for the chart */ @@ -162,42 +168,30 @@ function getBaselineBadgeOverflow( /** * Document count chart with draggable brushes to select time ranges * by default use `Baseline` and `Deviation` for the badge names - * @param dependencies - List of Kibana services that are required as dependencies - * @param brushSelectionUpdateHandler - Optional callback function which gets called the brush selection has changed - * @param width - Optional width - * @param chartPoints - Data chart points - * @param chartPointsSplit - Data chart points split - * @param timeRangeEarliest - Start time range for the chart - * @param timeRangeLatest - Ending time range for the chart - * @param interval - Time interval for the document count buckets - * @param chartPointsSplitLabel - Label to name the adjustedChartPointsSplit histogram - * @param isBrushCleared - Whether or not brush has been reset - * @param autoAnalysisStart - Timestamp for start of initial analysis - * @param barColorOverride - Optional color override for the default bar color for charts - * @param barStyleAccessor - Optional style to override bar chart - * @param barHighlightColorOverride - Optional color override for the highlighted bar color for charts - * @param deviationBrush - Optional settings override for the 'deviation' brush - * @param baselineBrush - Optional settings override for the 'baseline' brush - * @constructor + * + * @param props DocumentCountChart component props + * @returns The DocumentCountChart component. */ -export const DocumentCountChart: FC = ({ - dependencies, - brushSelectionUpdateHandler, - width, - chartPoints, - chartPointsSplit, - timeRangeEarliest, - timeRangeLatest, - interval, - chartPointsSplitLabel, - isBrushCleared, - autoAnalysisStart, - barColorOverride, - barStyleAccessor, - barHighlightColorOverride, - deviationBrush = {}, - baselineBrush = {}, -}) => { +export const DocumentCountChart: FC = (props) => { + const { + dependencies, + brushSelectionUpdateHandler, + width, + chartPoints, + chartPointsSplit, + timeRangeEarliest, + timeRangeLatest, + interval, + chartPointsSplitLabel, + isBrushCleared, + autoAnalysisStart, + barColorOverride, + barStyleAccessor, + barHighlightColorOverride, + deviationBrush = {}, + baselineBrush = {}, + } = props; + const { data, uiSettings, fieldFormats, charts } = dependencies; const chartTheme = charts.theme.useChartsTheme(); @@ -333,8 +327,13 @@ export const DocumentCountChart: FC = ({ const wpSnap = getSnappedWindowParameters(wp, snapTimestamps); setOriginalWindowParameters(wpSnap); setWindowParameters(wpSnap); + if (brushSelectionUpdateHandler !== undefined) { - brushSelectionUpdateHandler(wpSnap, true); + brushSelectionUpdateHandler( + wpSnap, + true, + getLogRateAnalysisType(adjustedChartPoints, wpSnap) + ); } } } @@ -385,7 +384,7 @@ export const DocumentCountChart: FC = ({ } setWindowParameters(wp); setWindowParametersAsPixels(wpPx); - brushSelectionUpdateHandler(wp, false); + brushSelectionUpdateHandler(wp, false, getLogRateAnalysisType(adjustedChartPoints, wp)); } const [mlBrushWidth, setMlBrushWidth] = useState(); diff --git a/x-pack/packages/ml/aiops_components/src/document_count_chart/index.ts b/x-pack/packages/ml/aiops_components/src/document_count_chart/index.ts index 808496f810782..5b64acedbf5f6 100644 --- a/x-pack/packages/ml/aiops_components/src/document_count_chart/index.ts +++ b/x-pack/packages/ml/aiops_components/src/document_count_chart/index.ts @@ -6,4 +6,8 @@ */ export { DocumentCountChart } from './document_count_chart'; -export type { DocumentCountChartPoint, DocumentCountChartProps } from './document_count_chart'; +export type { + BrushSelectionUpdateHandler, + BrushSettings, + DocumentCountChartProps, +} from './document_count_chart'; diff --git a/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx b/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx index 05635405e39d9..7288b120aa613 100644 --- a/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx +++ b/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx @@ -6,7 +6,7 @@ */ import { isEqual } from 'lodash'; -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, type FC } from 'react'; import * as d3Brush from 'd3-brush'; import * as d3Scale from 'd3-scale'; @@ -54,6 +54,9 @@ const BRUSH_MARGIN = 4; const BRUSH_HANDLE_SIZE = 4; const BRUSH_HANDLE_ROUNDED_CORNER = 2; +/** + * Props for the DualBrush React Component + */ interface DualBrushProps { /** * Min and max numeric timestamps for the two brushes @@ -88,40 +91,12 @@ interface DualBrushProps { /** * DualBrush React Component * Dual brush component that overlays the document count chart - * @type {FC} - * @param props - `DualBrushProps` component props - * @returns {React.ReactElement} The DualBrush component. + * + * @param props DualBrushProps component props + * @returns The DualBrush component. */ -export function DualBrush({ - /** - * Min and max numeric timestamps for the two brushes - */ - windowParameters, - /** - * Min timestamp for x domain - */ - min, - /** - * Max timestamp for x domain - */ - max, - /** - * Callback function whenever the brush changes - */ - onChange, - /** - * Margin left - */ - marginLeft, - /** - * Nearest timestamps to snap to the brushes to - */ - snapTimestamps, - /** - * Width - */ - width, -}: DualBrushProps) { +export const DualBrush: FC = (props) => { + const { windowParameters, min, max, onChange, marginLeft, snapTimestamps, width } = props; const d3BrushContainer = useRef(null); const brushes = useRef([]); @@ -383,4 +358,4 @@ export function DualBrush({ )} ); -} +}; diff --git a/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush_annotation.tsx b/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush_annotation.tsx index f78bfc78f3cce..5970aea37f5f6 100644 --- a/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush_annotation.tsx +++ b/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush_annotation.tsx @@ -21,11 +21,12 @@ interface BrushAnnotationProps { /** * DualBrushAnnotation React Component * Dual brush annotation component that overlays the document count chart - * @type {FC} - * @param props - `BrushAnnotationProps` component props - * @returns {React.ReactElement} The DualBrushAnnotation component. + * + * @param props BrushAnnotationProps component props + * @returns The DualBrushAnnotation component. */ -export const DualBrushAnnotation: FC = ({ id, min, max, style }) => { +export const DualBrushAnnotation: FC = (props) => { + const { id, min, max, style } = props; const { euiTheme } = useEuiTheme(); const { colors } = euiTheme; diff --git a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx index 7083282d3609f..4a2e38a4f3acf 100644 --- a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx +++ b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx @@ -44,38 +44,25 @@ interface ProgressControlProps { /** * ProgressControls React Component * Component with ability to Run & cancel analysis - * by default use `Baseline` and `Deviation` for the badge name - * @type {FC} - * @param children - List of Kibana services that are required as dependencies - * @param brushSelectionUpdateHandler - Optional callback function which gets called the brush selection has changed - * @param width - Optional width - * @param chartPoints - Data chart points - * @param chartPointsSplit - Data chart points split - * @param timeRangeEarliest - Start time range for the chart - * @param timeRangeLatest - Ending time range for the chart - * @param interval - Time interval for the document count buckets - * @param chartPointsSplitLabel - Label to name the adjustedChartPointsSplit histogram - * @param isBrushCleared - Whether or not brush has been reset - * @param autoAnalysisStart - Timestamp for start of initial analysis - * @param barColorOverride - Optional color override for the default bar color for charts - * @param barStyleAccessor - Optional style to override bar chart - * @param barHighlightColorOverride - Optional color override for the highlighted bar color for charts - * @param deviationBrush - Optional settings override for the 'deviation' brush - * @param baselineBrush - Optional settings override for the 'baseline' brush - * @returns {React.ReactElement} The ProgressControls component. + * by default uses `Baseline` and `Deviation` for the badge name + * + * @param props ProgressControls component props + * @returns The ProgressControls component. */ -export const ProgressControls: FC = ({ - children, - isBrushCleared, - progress, - progressMessage, - onRefresh, - onCancel, - onReset, - isRunning, - shouldRerunAnalysis, - runAnalysisDisabled = false, -}) => { +export const ProgressControls: FC = (props) => { + const { + children, + isBrushCleared, + progress, + progressMessage, + onRefresh, + onCancel, + onReset, + isRunning, + shouldRerunAnalysis, + runAnalysisDisabled = false, + } = props; + const { euiTheme } = useEuiTheme(); const runningProgressBarStyles = useAnimatedProgressBarBackground(euiTheme.colors.success); const analysisCompleteStyle = { display: 'none' }; diff --git a/x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.test.ts b/x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.test.ts new file mode 100644 index 0000000000000..84f9275128552 --- /dev/null +++ b/x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LogRateHistogramItem } from './log_rate_histogram_item'; +import { getLogRateAnalysisType } from './get_log_rate_analysis_type'; + +describe('getLogRateAnalysisType', () => { + const LogRateHistogramMock: LogRateHistogramItem[] = [ + { time: 0, value: 10 }, + { time: 1, value: 10 }, + { time: 2, value: 10 }, + { time: 3, value: 5 }, + { time: 4, value: 10 }, + { time: 5, value: 10 }, + { time: 6, value: 10 }, + { time: 7, value: 20 }, + { time: 8, value: 10 }, + { time: 9, value: 10 }, + ]; + + test('returns "spike" for the given parameters', () => { + expect( + getLogRateAnalysisType(LogRateHistogramMock, { + baselineMin: 4, + baselineMax: 6, + deviationMin: 7, + deviationMax: 8, + }) + ).toBe('spike'); + }); + + test('returns "dip" for the given parameters', () => { + expect( + getLogRateAnalysisType(LogRateHistogramMock, { + baselineMin: 0, + baselineMax: 2, + deviationMin: 3, + deviationMax: 4, + }) + ).toBe('dip'); + }); + + test('falls back to "spike" if both time range have the same median', () => { + expect( + getLogRateAnalysisType(LogRateHistogramMock, { + baselineMin: 0, + baselineMax: 2, + deviationMin: 4, + deviationMax: 6, + }) + ).toBe('spike'); + }); +}); diff --git a/x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.ts b/x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.ts new file mode 100644 index 0000000000000..0303d903da05b --- /dev/null +++ b/x-pack/packages/ml/aiops_utils/get_log_rate_analysis_type.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { median } from 'd3-array'; + +import { LOG_RATE_ANALYSIS_TYPE, type LogRateAnalysisType } from './log_rate_analysis_type'; +import type { LogRateHistogramItem } from './log_rate_histogram_item'; +import type { WindowParameters } from './window_parameters'; + +/** + * Identify the log rate analysis type based on the baseline/deviation + * time ranges on a given log rate histogram. + * + * @param logRateHistogram The log rate histogram. + * @param windowParameters The window parameters with baseline and deviation time range. + * @returns The log rate analysis type. + */ +export function getLogRateAnalysisType( + logRateHistogram: LogRateHistogramItem[], + windowParameters: WindowParameters +): LogRateAnalysisType { + const { baselineMin, baselineMax, deviationMin, deviationMax } = windowParameters; + const baselineItems = logRateHistogram.filter( + (d) => d.time >= baselineMin && d.time < baselineMax + ); + const baselineMedian = median(baselineItems.map((d) => d.value)) ?? 0; + + const deviationItems = logRateHistogram.filter( + (d) => d.time >= deviationMin && d.time < deviationMax + ); + const deviationMedian = median(deviationItems.map((d) => d.value)) ?? 0; + + return deviationMedian >= baselineMedian + ? LOG_RATE_ANALYSIS_TYPE.SPIKE + : LOG_RATE_ANALYSIS_TYPE.DIP; +} diff --git a/x-pack/packages/ml/aiops_utils/index.ts b/x-pack/packages/ml/aiops_utils/index.ts index f2c343f2500ac..6d22ce5849ec6 100644 --- a/x-pack/packages/ml/aiops_utils/index.ts +++ b/x-pack/packages/ml/aiops_utils/index.ts @@ -5,5 +5,11 @@ * 2.0. */ -export { getSnappedWindowParameters, getWindowParameters } from './src/get_window_parameters'; -export type { WindowParameters } from './src/get_window_parameters'; +export { getLogRateAnalysisType } from './get_log_rate_analysis_type'; +export { LOG_RATE_ANALYSIS_TYPE, type LogRateAnalysisType } from './log_rate_analysis_type'; +export { type LogRateHistogramItem } from './log_rate_histogram_item'; +export { + getSnappedWindowParameters, + getWindowParameters, + type WindowParameters, +} from './window_parameters'; diff --git a/x-pack/packages/ml/aiops_utils/log_rate_analysis_type.ts b/x-pack/packages/ml/aiops_utils/log_rate_analysis_type.ts new file mode 100644 index 0000000000000..ba8f370029782 --- /dev/null +++ b/x-pack/packages/ml/aiops_utils/log_rate_analysis_type.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * The type of log rate analysis (spike or dip) will affect how parameters are + * passed to the analysis API endpoint. + */ +export const LOG_RATE_ANALYSIS_TYPE = { + SPIKE: 'spike', + DIP: 'dip', +} as const; + +/** + * Union type of log rate analysis types. + */ +export type LogRateAnalysisType = + typeof LOG_RATE_ANALYSIS_TYPE[keyof typeof LOG_RATE_ANALYSIS_TYPE]; diff --git a/x-pack/packages/ml/aiops_utils/log_rate_histogram_item.ts b/x-pack/packages/ml/aiops_utils/log_rate_histogram_item.ts new file mode 100644 index 0000000000000..86368346ddde0 --- /dev/null +++ b/x-pack/packages/ml/aiops_utils/log_rate_histogram_item.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. + */ + +/** + * Log rate histogram item + */ +export interface LogRateHistogramItem { + /** + * Time of bucket + */ + time: number | string; + /** + * Number of doc count for that time bucket + */ + value: number; +} diff --git a/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts b/x-pack/packages/ml/aiops_utils/window_parameters.ts similarity index 100% rename from x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts rename to x-pack/packages/ml/aiops_utils/window_parameters.ts diff --git a/x-pack/plugins/aiops/common/constants.ts b/x-pack/plugins/aiops/common/constants.ts index b6ab2d3e6e34b..c8831aa06b330 100644 --- a/x-pack/plugins/aiops/common/constants.ts +++ b/x-pack/plugins/aiops/common/constants.ts @@ -10,22 +10,12 @@ */ export const LOG_RATE_ANALYSIS_P_VALUE_THRESHOLD = 0.02; -/** - * The type of log rate analysis (spike or dip) will affect how parameters are - * passed to the analysis API endpoint. - */ -export const LOG_RATE_ANALYSIS_TYPE = { - SPIKE: 'spike', - DIP: 'dip', -} as const; -/** - * Union type of log rate analysis types. - */ -export type LogRateAnalysisType = - typeof LOG_RATE_ANALYSIS_TYPE[keyof typeof LOG_RATE_ANALYSIS_TYPE]; - /** * For the technical preview of Log Rate Analysis we use a hard coded seed. * In future versions we might use a user specific seed or let the user customise it. */ export const RANDOM_SAMPLER_SEED = 3867412; + +export const CASES_ATTACHMENT_CHANGE_POINT_CHART = 'aiopsChangePointChart'; + +export const EMBEDDABLE_CHANGE_POINT_CHART_TYPE = 'aiopsChangePointChart' as const; diff --git a/x-pack/plugins/aiops/common/index.ts b/x-pack/plugins/aiops/common/index.ts index 2d41737dbc45e..d9d9b9cb4d385 100755 --- a/x-pack/plugins/aiops/common/index.ts +++ b/x-pack/plugins/aiops/common/index.ts @@ -5,8 +5,6 @@ * 2.0. */ -export type { LogRateAnalysisType } from './constants'; - /** * PLUGIN_ID is used as a unique identifier for the aiops plugin */ diff --git a/x-pack/plugins/aiops/kibana.jsonc b/x-pack/plugins/aiops/kibana.jsonc index 3e86674831d48..02558e7329093 100644 --- a/x-pack/plugins/aiops/kibana.jsonc +++ b/x-pack/plugins/aiops/kibana.jsonc @@ -13,11 +13,20 @@ "lens", "licensing", "uiActions", + "embeddable", + "presentationUtil", + "dashboard", + "fieldFormats" + ], + "optionalPlugins": [ + "cases" ], "requiredBundles": [ "fieldFormats", "kibanaReact", - "kibanaUtils" + "kibanaUtils", + "embeddable", + "cases" ], "extraPublicDirs": [ "common" diff --git a/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx b/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx new file mode 100644 index 0000000000000..4aa830328e805 --- /dev/null +++ b/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { memoize } from 'lodash'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import React, { FC } from 'react'; +import { PersistableStateAttachmentViewProps } from '@kbn/cases-plugin/public/client/attachment_framework/types'; +import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiDescriptionList } from '@elastic/eui'; +import deepEqual from 'fast-deep-equal'; +import { EmbeddableChangePointChartProps } from '../embeddable'; + +export const initComponent = memoize( + (fieldFormats: FieldFormatsStart, EmbeddableComponent: FC) => { + return React.memo( + (props: PersistableStateAttachmentViewProps) => { + const { persistableStateAttachmentState } = props; + + const dataFormatter = fieldFormats.deserialize({ + id: FIELD_FORMAT_IDS.DATE, + }); + + const inputProps = + persistableStateAttachmentState as unknown as EmbeddableChangePointChartProps; + + const listItems = [ + { + title: ( + + ), + description: `${dataFormatter.convert( + inputProps.timeRange.from + )} - ${dataFormatter.convert(inputProps.timeRange.to)}`, + }, + ]; + + return ( + <> + + + + ); + }, + (prevProps, nextProps) => + deepEqual( + prevProps.persistableStateAttachmentState, + nextProps.persistableStateAttachmentState + ) + ); + } +); diff --git a/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx b/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx new file mode 100644 index 0000000000000..cc70d7ff98a3b --- /dev/null +++ b/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { FormattedMessage } from '@kbn/i18n-react'; +import type { CasesUiSetup } from '@kbn/cases-plugin/public'; +import type { CoreStart } from '@kbn/core/public'; +import { CASES_ATTACHMENT_CHANGE_POINT_CHART } from '../../common/constants'; +import { getEmbeddableChangePointChart } from '../embeddable/embeddable_change_point_chart_component'; +import { AiopsPluginStartDeps } from '../types'; + +export function registerChangePointChartsAttachment( + cases: CasesUiSetup, + coreStart: CoreStart, + pluginStart: AiopsPluginStartDeps +) { + const EmbeddableComponent = getEmbeddableChangePointChart(coreStart, pluginStart); + + cases.attachmentFramework.registerPersistableState({ + id: CASES_ATTACHMENT_CHANGE_POINT_CHART, + icon: 'machineLearningApp', + displayName: i18n.translate('xpack.aiops.changePointDetection.cases.attachmentName', { + defaultMessage: 'Change point chart', + }), + getAttachmentViewObject: () => ({ + event: ( + + ), + timelineAvatar: 'machineLearningApp', + children: React.lazy(async () => { + const { initComponent } = await import('./change_point_charts_attachment'); + + return { + default: initComponent(pluginStart.fieldFormats, EmbeddableComponent), + }; + }), + }), + }); +} diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx index 74294085db74a..7dcc96f59b05d 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx @@ -21,6 +21,7 @@ import { usePageUrlState } from '@kbn/ml-url-state'; import { useTimefilter, useTimeRangeUpdates } from '@kbn/ml-date-picker'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import { type QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; +import { FilterQueryContextProvider } from '../../hooks/use_filters_query'; import { type ChangePointType, DEFAULT_AGG_FUNCTION } from './constants'; import { createMergedEsQuery, @@ -254,7 +255,7 @@ export const ChangePointDetectionContextProvider: FC = ({ children }) => { return ( - {children} + {children} ); }; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_page.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_page.tsx index 15f618946cb8d..203c841a67436 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_page.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_page.tsx @@ -21,7 +21,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { Query } from '@kbn/es-query'; -import { ChartsGrid } from './charts_grid'; +import { ChartsGridContainer } from './charts_grid'; import { FieldsConfig } from './fields_config'; import { useDataSource } from '../../hooks/use_data_source'; import { ChangePointTypeFilter } from './change_point_type_filter'; @@ -163,7 +163,7 @@ export const ChangePointDetectionPage: FC = () => { - + ) : null} diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx index 508c42b31a876..b207a1186a237 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx @@ -5,9 +5,10 @@ * 2.0. */ -import React, { FC } from 'react'; +import React, { FC, useMemo } from 'react'; +import { map } from 'rxjs/operators'; import { pick } from 'lodash'; - +import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common'; import { EuiSpacer } from '@elastic/eui'; import { DataView } from '@kbn/data-views-plugin/common'; @@ -15,10 +16,11 @@ import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import { StorageContextProvider } from '@kbn/ml-local-storage'; import { UrlStateProvider } from '@kbn/ml-url-state'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { DatePickerContextProvider } from '@kbn/ml-date-picker'; +import { DatePickerContextProvider, mlTimefilterRefresh$ } from '@kbn/ml-date-picker'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import { type Observable } from 'rxjs'; import { DataSourceContext } from '../../hooks/use_data_source'; import { AiopsAppContext, AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; import { AIOPS_STORAGE_KEYS } from '../../types/storage'; @@ -28,6 +30,7 @@ import { PageHeader } from '../page_header'; import { ChangePointDetectionPage } from './change_point_detection_page'; import { ChangePointDetectionContextProvider } from './change_point_detection_context'; import { timeSeriesDataViewWarning } from '../../application/utils/time_series_dataview_check'; +import { ReloadContextProvider } from '../../hooks/use_reload'; const localStorage = new Storage(window.localStorage); @@ -57,25 +60,43 @@ export const ChangePointDetectionAppState: FC const warning = timeSeriesDataViewWarning(dataView, 'change_point_detection'); + const reload$ = useMemo>(() => { + return mlTimefilterRefresh$.pipe(map((v) => v.lastRefresh)); + }, []); + if (warning !== null) { return <>{warning}; } + const PresentationContextProvider = + appDependencies.presentationUtil?.ContextProvider ?? React.Fragment; + + const CasesContext = appDependencies.cases?.ui.getCasesContext() ?? React.Fragment; + const casesPermissions = appDependencies.cases?.helpers.canUseCases(); + return ( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + ); }; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx index bed4d0ad31ca8..84c5723548ee2 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx @@ -19,12 +19,14 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; import { type Filter, FilterStateStore } from '@kbn/es-query'; +import { NoChangePointsWarning } from './no_change_points_warning'; import { useDataSource } from '../../hooks/use_data_source'; import { useCommonChartProps } from './use_common_chart_props'; import { type ChangePointAnnotation, FieldConfig, SelectedChangePoint, + useChangePointDetectionContext, } from './change_point_detection_context'; import { type ChartComponentProps } from './chart_component'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; @@ -92,6 +94,8 @@ export const ChangePointsTable: FC = ({ const hasActions = fieldConfig.splitField !== undefined; + const { bucketInterval } = useChangePointDetectionContext(); + const columns: Array> = [ { id: 'timestamp', @@ -122,7 +126,13 @@ export const ChangePointsTable: FC = ({ ['&.euiTableCellContent']: { display: 'block', padding: 0 }, }, render: (annotation: ChangePointAnnotation) => { - return ; + return ( + + ); }, }, { @@ -283,25 +293,7 @@ export const ChangePointsTable: FC = ({ } /> ) : ( - - -

- } - body={ -

- -

- } - /> + ) } /> @@ -313,10 +305,13 @@ export const MiniChartPreview: FC = ({ fieldConfig, annotat lens: { EmbeddableComponent }, } = useAiopsAppContext(); + const { bucketInterval } = useChangePointDetectionContext(); + const { filters, query, attributes, timeRange } = useCommonChartProps({ annotation, fieldConfig, previewMode: true, + bucketInterval: bucketInterval.expression, }); return ( diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/chart_component.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/chart_component.tsx index 0866fe325b40a..323f59c6c5646 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/chart_component.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/chart_component.tsx @@ -6,6 +6,7 @@ */ import React, { FC } from 'react'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; import { useCommonChartProps } from './use_common_chart_props'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import type { ChangePointAnnotation, FieldConfig } from './change_point_detection_context'; @@ -13,33 +14,48 @@ import type { ChangePointAnnotation, FieldConfig } from './change_point_detectio export interface ChartComponentProps { fieldConfig: FieldConfig; annotation: ChangePointAnnotation; + + interval: string; +} + +export interface ChartComponentPropsAll { + fn: string; + metricField: string; + splitField?: string; + maxResults: number; + timeRange: TimeRange; + filters?: Filter[]; + query?: Query; } -export const ChartComponent: FC = React.memo(({ annotation, fieldConfig }) => { - const { - lens: { EmbeddableComponent }, - } = useAiopsAppContext(); +export const ChartComponent: FC = React.memo( + ({ annotation, fieldConfig, interval }) => { + const { + lens: { EmbeddableComponent }, + } = useAiopsAppContext(); - const { filters, timeRange, query, attributes } = useCommonChartProps({ - fieldConfig, - annotation, - }); + const { filters, timeRange, query, attributes } = useCommonChartProps({ + fieldConfig, + annotation, + bucketInterval: interval, + }); - return ( - - ); -}); + return ( + + ); + } +); diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx index 303dea7f03f9c..06879622a13fe 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/charts_grid.tsx @@ -23,7 +23,10 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { useTimefilter } from '@kbn/ml-date-picker'; import { type RefreshInterval } from '@kbn/data-plugin/common'; -import { type SelectedChangePoint } from './change_point_detection_context'; +import { + type SelectedChangePoint, + useChangePointDetectionContext, +} from './change_point_detection_context'; import { ChartComponent } from './chart_component'; const CHARTS_PER_PAGE = 6; @@ -32,11 +35,115 @@ interface ChartsGridProps { changePoints: Record; } -export const ChartsGrid: FC = ({ changePoints: changePointsDict }) => { +/** + * Shared component for change point charts grid. + * Used both in AIOps UI and inside embeddable. + * + * @param changePoints + * @constructor + */ +export const ChartsGrid: FC<{ changePoints: SelectedChangePoint[]; interval: string }> = ({ + changePoints, + interval, +}) => { + return ( + = 2 ? 2 : 1} + responsive + gutterSize={'m'} + css={{ width: '100%' }} + > + {changePoints.map((v, index) => { + const key = `${index}_${v.group?.value ?? 'single_metric'}_${v.fn}_${v.metricField}_${ + v.timestamp + }_${v.p_value}`; + return ( + + + + + {v.group ? ( + + ) : null} + + {v.reason ? ( + + + + ) : null} + + + + {v.fn}({v.metricField}) + + + + + + + + {v.p_value !== undefined ? ( + + + ), + description: v.p_value.toPrecision(3), + }, + ]} + /> + + ) : null} + + {v.type} + + + + + + + ); + })} + + ); +}; + +/** + * Wrapper component for change point charts grid. + * + * @param changePointsDict + * @constructor + */ +export const ChartsGridContainer: FC = ({ changePoints: changePointsDict }) => { const timefilter = useTimefilter(); const initialRefreshSetting = useRef(); + const { bucketInterval } = useChangePointDetectionContext(); + useEffect( function pauseRefreshOnMount() { initialRefreshSetting.current = timefilter.getRefreshInterval(); @@ -76,85 +183,7 @@ export const ChartsGrid: FC = ({ changePoints: changePointsDict return ( <> - = 2 ? 2 : 1} responsive gutterSize={'m'}> - {resultPerPage.map((v, index) => { - const key = `${index}_${v.group?.value ?? 'single_metric'}_${v.fn}_${v.metricField}_${ - v.timestamp - }_${v.p_value}`; - return ( - - - - - {v.group ? ( - - ) : null} - - {v.reason ? ( - - - - ) : null} - - - - {v.fn}({v.metricField}) - - - - - - - - {v.p_value !== undefined ? ( - - - ), - description: v.p_value.toPrecision(3), - }, - ]} - /> - - ) : null} - - {v.type} - - - - - - - ); - })} - + {pagination.pageCount > 1 ? ( diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx index 732b547e93026..e2e0bf0a6d328 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx @@ -10,16 +10,35 @@ import { EuiButton, EuiButtonIcon, EuiCallOut, + EuiContextMenu, + EuiFieldNumber, EuiFlexGroup, EuiFlexItem, + EuiForm, + EuiFormRow, + EuiIcon, EuiPanel, + EuiPopover, EuiProgress, EuiSpacer, + EuiSwitch, + EuiToolTip, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats'; import { useTimefilter, useTimeRangeUpdates } from '@kbn/ml-date-picker'; +import { + LazySavedObjectSaveModalDashboard, + SaveModalDashboardProps, + withSuspense, +} from '@kbn/presentation-util-plugin/public'; +import { EuiContextMenuProps } from '@elastic/eui/src/components/context_menu/context_menu'; +import { isDefined } from '@kbn/ml-is-defined'; +import { numberValidator } from '@kbn/ml-agg-utils'; +import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../../common/constants'; +import { useCasesModal } from '../../hooks/use_cases_modal'; +import { type EmbeddableChangePointChartInput } from '../../embeddable/embeddable_change_point_chart'; import { useDataSource } from '../../hooks/use_data_source'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { ChangePointsTable } from './change_points_table'; @@ -35,9 +54,12 @@ import { } from './change_point_detection_context'; import { useChangePointResults } from './use_change_point_agg_request'; import { useSplitFieldCardinality } from './use_split_field_cardinality'; +import { MAX_SERIES } from '../../embeddable/const'; const selectControlCss = { width: '350px' }; +const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard); + /** * Contains panels with controls and change point results. */ @@ -93,6 +115,7 @@ export const FieldsConfig: FC = () => { return ( onChange(value, index)} @@ -121,6 +144,7 @@ export const FieldsConfig: FC = () => { }; export interface FieldPanelProps { + panelIndex: number; fieldConfig: FieldConfig; removeDisabled: boolean; onChange: (update: FieldConfig) => void; @@ -138,6 +162,7 @@ export interface FieldPanelProps { * @constructor */ const FieldPanel: FC = ({ + panelIndex, fieldConfig, onChange, onRemove, @@ -145,18 +170,315 @@ const FieldPanel: FC = ({ onSelectionChange, 'data-test-subj': dataTestSubj, }) => { - const { combinedQuery, requestParams } = useChangePointDetectionContext(); + const { + embeddable, + application: { capabilities }, + cases, + } = useAiopsAppContext(); + + const { dataView } = useDataSource(); + + const { combinedQuery, requestParams, selectedChangePoints } = useChangePointDetectionContext(); const splitFieldCardinality = useSplitFieldCardinality(fieldConfig.splitField, combinedQuery); const [isExpanded, setIsExpanded] = useState(true); + const [isActionMenuOpen, setIsActionMenuOpen] = useState(false); + + const canEditDashboards = capabilities.dashboard?.createNew ?? false; + const { create: canCreateCase, update: canUpdateCase } = cases?.helpers?.canUseCases() ?? { + create: false, + update: false, + }; + + const [dashboardAttachment, setDashboardAttachment] = useState<{ + applyTimeRange: boolean; + maxSeriesToPlot: number; + }>({ + applyTimeRange: false, + maxSeriesToPlot: 6, + }); + const [dashboardAttachmentReady, setDashboardAttachmentReady] = useState(false); + const { results: annotations, isLoading: annotationsLoading, progress, } = useChangePointResults(fieldConfig, requestParams, combinedQuery, splitFieldCardinality); + const openCasesModalCallback = useCasesModal(EMBEDDABLE_CHANGE_POINT_CHART_TYPE); + + const selectedPartitions = useMemo(() => { + return (selectedChangePoints[panelIndex] ?? []).map((v) => v.group?.value as string); + }, [selectedChangePoints, panelIndex]); + + const caseAttachmentButtonDisabled = + isDefined(fieldConfig.splitField) && selectedPartitions.length === 0; + + const timeRange = useTimeRangeUpdates(); + + const maxSeriesValidator = useMemo( + () => numberValidator({ min: 1, max: MAX_SERIES, integerOnly: true }), + [] + ); + + const maxSeriesInvalid = maxSeriesValidator(dashboardAttachment.maxSeriesToPlot) !== null; + + const panels = useMemo(() => { + return [ + { + id: 'panelActions', + size: 's', + items: [ + ...(canEditDashboards || canUpdateCase || canCreateCase + ? [ + { + name: + selectedPartitions.length > 0 + ? i18n.translate( + 'xpack.aiops.changePointDetection.attachSelectedChartsLabel', + { + defaultMessage: 'Attach selected charts', + } + ) + : i18n.translate('xpack.aiops.changePointDetection.attachChartsLabel', { + defaultMessage: 'Attach charts', + }), + icon: 'plusInCircle', + panel: 'attachMainPanel', + }, + ] + : []), + { + name: i18n.translate('xpack.aiops.changePointDetection.removeConfigLabel', { + defaultMessage: 'Remove configuration', + }), + icon: 'trash', + onClick: onRemove, + disabled: removeDisabled, + }, + ], + }, + { + id: 'attachMainPanel', + size: 's', + initialFocusedItemIndex: 0, + title: + selectedPartitions.length > 0 + ? i18n.translate('xpack.aiops.changePointDetection.attachSelectedChartsLabel', { + defaultMessage: 'Attach selected charts', + }) + : i18n.translate('xpack.aiops.changePointDetection.attachChartsLabel', { + defaultMessage: 'Attach charts', + }), + items: [ + ...(canEditDashboards + ? [ + { + name: i18n.translate('xpack.aiops.changePointDetection.attachToDashboardLabel', { + defaultMessage: 'To dashboard', + }), + panel: 'attachToDashboardPanel', + }, + ] + : []), + ...(canUpdateCase || canCreateCase + ? [ + { + name: i18n.translate('xpack.aiops.changePointDetection.attachToCaseLabel', { + defaultMessage: 'To case', + }), + disabled: caseAttachmentButtonDisabled, + ...(caseAttachmentButtonDisabled + ? { + toolTipPosition: 'left' as const, + toolTipContent: i18n.translate( + 'xpack.aiops.changePointDetection.attachToCaseTooltipContent', + { + defaultMessage: 'Select change points to attach', + } + ), + } + : {}), + onClick: () => { + openCasesModalCallback({ + timeRange, + fn: fieldConfig.fn, + metricField: fieldConfig.metricField, + dataViewId: dataView.id, + ...(fieldConfig.splitField + ? { + splitField: fieldConfig.splitField, + partitions: selectedPartitions, + } + : {}), + }); + }, + }, + ] + : []), + ], + }, + { + id: 'attachToDashboardPanel', + title: i18n.translate('xpack.aiops.changePointDetection.attachToDashboardTitle', { + defaultMessage: 'Attach to dashboard', + }), + size: 's', + content: ( + + + + + + setDashboardAttachment((prevState) => { + return { + ...prevState, + applyTimeRange: e.target.checked, + }; + }) + } + compressed + /> + + {isDefined(fieldConfig.splitField) && selectedPartitions.length === 0 ? ( + + } + label={ + + + + + + + + + + + } + > + + setDashboardAttachment((prevState) => { + return { + ...prevState, + maxSeriesToPlot: Number(e.target.value), + }; + }) + } + min={1} + max={MAX_SERIES} + /> + + ) : null} + + + + + + + + + ), + }, + ]; + }, [ + canCreateCase, + canEditDashboards, + canUpdateCase, + caseAttachmentButtonDisabled, + dashboardAttachment.applyTimeRange, + dashboardAttachment.maxSeriesToPlot, + dataView.id, + fieldConfig.fn, + fieldConfig.metricField, + fieldConfig.splitField, + onRemove, + openCasesModalCallback, + removeDisabled, + selectedPartitions, + timeRange, + maxSeriesInvalid, + ]); + + const onSaveCallback: SaveModalDashboardProps['onSave'] = useCallback( + ({ dashboardId, newTitle, newDescription }) => { + const stateTransfer = embeddable!.getStateTransfer(); + + const embeddableInput: Partial = { + title: newTitle, + description: newDescription, + dataViewId: dataView.id, + metricField: fieldConfig.metricField, + splitField: fieldConfig.splitField, + fn: fieldConfig.fn, + ...(dashboardAttachment.applyTimeRange ? { timeRange } : {}), + maxSeriesToPlot: dashboardAttachment.maxSeriesToPlot, + ...(selectedChangePoints[panelIndex]?.length ? { partitions: selectedPartitions } : {}), + }; + + const state = { + input: embeddableInput, + type: EMBEDDABLE_CHANGE_POINT_CHART_TYPE, + }; + + const path = dashboardId === 'new' ? '#/create' : `#/view/${dashboardId}`; + + stateTransfer.navigateToWithEmbeddablePackage('dashboards', { + state, + path, + }); + }, + [ + embeddable, + dataView.id, + fieldConfig.metricField, + fieldConfig.splitField, + fieldConfig.fn, + dashboardAttachment.applyTimeRange, + dashboardAttachment.maxSeriesToPlot, + timeRange, + selectedChangePoints, + panelIndex, + selectedPartitions, + ] + ); + return ( @@ -197,15 +519,32 @@ const FieldPanel: FC = ({
- + + + + } + isOpen={isActionMenuOpen} + closePopover={setIsActionMenuOpen.bind(null, false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + +
@@ -218,6 +557,34 @@ const FieldPanel: FC = ({ onSelectionChange={onSelectionChange} /> ) : null} + + {dashboardAttachmentReady ? ( + { + setDashboardAttachmentReady(false); + }} + onSave={onSaveCallback} + /> + ) : null}
); }; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/no_change_points_warning.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/no_change_points_warning.tsx new file mode 100644 index 0000000000000..0f5303725e514 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/change_point_detection/no_change_points_warning.tsx @@ -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 React, { type FC } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +export const NoChangePointsWarning: FC = () => { + return ( + + + + } + body={ +

+ +

+ } + /> + ); +}; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/use_change_point_agg_request.ts b/x-pack/plugins/aiops/public/components/change_point_detection/use_change_point_agg_request.ts index cede70de9e11b..35096d303e61a 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/use_change_point_agg_request.ts +++ b/x-pack/plugins/aiops/public/components/change_point_detection/use_change_point_agg_request.ts @@ -8,8 +8,8 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { type QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; -import { useRefresh } from '@kbn/ml-date-picker'; import { isDefined } from '@kbn/ml-is-defined'; +import { useReload } from '../../hooks/use_reload'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { ChangePointAnnotation, @@ -102,7 +102,7 @@ function getChangePointDetectionRequestBody( index, size: 0, body: { - query, + ...(query ? { query } : {}), aggregations, }, }, @@ -121,7 +121,7 @@ export function useChangePointResults( const { dataView } = useDataSource(); - const refresh = useRefresh(); + const { refreshTimestamp: refresh } = useReload(); const [results, setResults] = useState([]); /** @@ -164,6 +164,7 @@ export function useChangePointResults( }, query ); + const result = await runRequest< typeof requestPayload, { rawResponse: ChangePointAggResponse } diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts b/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts index 96a5b6cec1c45..85f3285e17977 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts +++ b/x-pack/plugins/aiops/public/components/change_point_detection/use_common_chart_props.ts @@ -8,31 +8,30 @@ import moment from 'moment'; import { FilterStateStore, type TimeRange } from '@kbn/es-query'; import { type TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { useTimeRangeUpdates } from '@kbn/ml-date-picker'; import { useMemo } from 'react'; +import { useFilerQueryUpdates } from '../../hooks/use_filters_query'; import { fnOperationTypeMapping } from './constants'; import { useDataSource } from '../../hooks/use_data_source'; -import { - ChangePointAnnotation, - FieldConfig, - useChangePointDetectionContext, -} from './change_point_detection_context'; +import { ChangePointAnnotation, FieldConfig } from './change_point_detection_context'; /** * Provides common props for the Lens Embeddable component + * based on the change point definition and currently applied filters and query. */ export const useCommonChartProps = ({ annotation, fieldConfig, previewMode = false, + bucketInterval, }: { fieldConfig: FieldConfig; annotation: ChangePointAnnotation; previewMode?: boolean; + bucketInterval: string; }): Partial => { - const timeRange = useTimeRangeUpdates(true); const { dataView } = useDataSource(); - const { bucketInterval, resultQuery, resultFilters } = useChangePointDetectionContext(); + + const { filters: resultFilters, query: resultQuery, timeRange } = useFilerQueryUpdates(); /** * In order to correctly render annotations for change points at the edges, @@ -48,6 +47,7 @@ export const useCommonChartProps = ({ const filters = useMemo(() => { return [ ...resultFilters, + // Adds a filter for change point partition value ...(annotation.group ? [ { @@ -161,7 +161,9 @@ export const useCommonChartProps = ({ outside: false, }, ], - ignoreGlobalFilters: true, + // TODO check if we need to set filter from + // the filterManager + ignoreGlobalFilters: false, }, ], }, @@ -180,7 +182,7 @@ export const useCommonChartProps = ({ isBucketed: true, scale: 'interval', params: { - interval: bucketInterval.expression, + interval: bucketInterval, includeEmptyRows: true, dropPartials: false, }, @@ -219,7 +221,7 @@ export const useCommonChartProps = ({ dataView.timeFieldName, resultQuery, filters, - bucketInterval.expression, + bucketInterval, fieldConfig.fn, fieldConfig.metricField, gridAndLabelsVisibility, diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx index abb7668384ca9..23149aeb7c58d 100644 --- a/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx @@ -13,8 +13,8 @@ import { RectAnnotationSpec, } from '@elastic/charts/dist/chart_types/xy_chart/utils/specs'; -import type { WindowParameters } from '@kbn/aiops-utils'; -import { DocumentCountChart, type DocumentCountChartPoint } from '@kbn/aiops-components'; +import type { LogRateHistogramItem, WindowParameters } from '@kbn/aiops-utils'; +import { DocumentCountChart, type BrushSelectionUpdateHandler } from '@kbn/aiops-components'; import { useAiopsAppContext } from '../../../hooks/use_aiops_app_context'; import { DocumentCountStats } from '../../../get_document_stats'; @@ -22,7 +22,7 @@ import { DocumentCountStats } from '../../../get_document_stats'; import { TotalCountHeader } from '../total_count_header'; export interface DocumentCountContentProps { - brushSelectionUpdateHandler: (d: WindowParameters, force: boolean) => void; + brushSelectionUpdateHandler: BrushSelectionUpdateHandler; documentCountStats?: DocumentCountStats; documentCountStatsSplit?: DocumentCountStats; documentCountStatsSplitLabel?: string; @@ -78,14 +78,14 @@ export const DocumentCountContent: FC = ({ ) : null; } - const chartPoints: DocumentCountChartPoint[] = Object.entries(documentCountStats.buckets).map( + const chartPoints: LogRateHistogramItem[] = Object.entries(documentCountStats.buckets).map( ([time, value]) => ({ time: +time, value, }) ); - let chartPointsSplit: DocumentCountChartPoint[] | undefined; + let chartPointsSplit: LogRateHistogramItem[] | undefined; if (documentCountStatsSplit?.buckets !== undefined) { chartPointsSplit = Object.entries(documentCountStatsSplit?.buckets).map(([time, value]) => ({ time: +time, diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx index 33180584256df..e27d3af3aefa7 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx @@ -15,11 +15,13 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { Dictionary } from '@kbn/ml-url-state'; -import type { WindowParameters } from '@kbn/aiops-utils'; +import { + LOG_RATE_ANALYSIS_TYPE, + type LogRateAnalysisType, + type WindowParameters, +} from '@kbn/aiops-utils'; import type { SignificantTerm } from '@kbn/ml-agg-utils'; -import { LOG_RATE_ANALYSIS_TYPE, type LogRateAnalysisType } from '../../../../common/constants'; - import { useData } from '../../../hooks/use_data'; import { DocumentCountContent } from '../../document_count_content/document_count_content'; @@ -48,8 +50,6 @@ export function getDocumentCountStatsSplitLabel( export interface LogRateAnalysisContentProps { /** The data view to analyze. */ dataView: DataView; - /** The type of analysis, whether it's a spike or dip */ - analysisType?: LogRateAnalysisType; setGlobalState?: (params: Dictionary) => void; /** Timestamp for the start of the range for initial analysis */ initialAnalysisStart?: number | WindowParameters; @@ -68,7 +68,6 @@ export interface LogRateAnalysisContentProps { export const LogRateAnalysisContent: FC = ({ dataView, - analysisType = LOG_RATE_ANALYSIS_TYPE.SPIKE, setGlobalState, initialAnalysisStart: incomingInitialAnalysisStart, timeRange, @@ -83,6 +82,9 @@ export const LogRateAnalysisContent: FC = ({ number | WindowParameters | undefined >(incomingInitialAnalysisStart); const [isBrushCleared, setIsBrushCleared] = useState(true); + const [logRateAnalysisType, setLogRateAnalysisType] = useState( + LOG_RATE_ANALYSIS_TYPE.SPIKE + ); useEffect(() => { setIsBrushCleared(windowParameters === undefined); @@ -111,13 +113,18 @@ export const LogRateAnalysisContent: FC = ({ const { sampleProbability, totalCount, documentCountStats, documentCountStatsCompare } = documentStats; - function brushSelectionUpdate(d: WindowParameters, force: boolean) { + function brushSelectionUpdate( + windowParametersUpdate: WindowParameters, + force: boolean, + logRateAnalysisTypeUpdate: LogRateAnalysisType + ) { if (!isBrushCleared || force) { - setWindowParameters(d); + setWindowParameters(windowParametersUpdate); } if (force) { setIsBrushCleared(false); } + setLogRateAnalysisType(logRateAnalysisTypeUpdate); } function clearSelection() { @@ -153,7 +160,7 @@ export const LogRateAnalysisContent: FC = ({ {earliest !== undefined && latest !== undefined && windowParameters !== undefined && ( = ({ dataView, - analysisType = LOG_RATE_ANALYSIS_TYPE.SPIKE, appDependencies, setGlobalState, initialAnalysisStart, @@ -100,7 +96,6 @@ export const LogRateAnalysisContentWrapper: FC = ({ const { clearAllRowState } = useLogRateAnalysisResultsTableRowContext(); + const [currentAnalysisType, setCurrentAnalysisType] = useState(); const [currentAnalysisWindowParameters, setCurrentAnalysisWindowParameters] = useState< WindowParameters | undefined >(); @@ -215,6 +221,7 @@ export const LogRateAnalysisResults: FC = ({ setOverrides(undefined); if (onAnalysisCompleted) { onAnalysisCompleted({ + analysisType, significantTerms: data.significantTerms, significantTermsGroups: data.significantTermsGroups, }); @@ -241,6 +248,7 @@ export const LogRateAnalysisResults: FC = ({ clearAllRowState(); } + setCurrentAnalysisType(analysisType); setCurrentAnalysisWindowParameters(windowParameters); // We trigger hooks updates above so we cannot directly call `start()` here @@ -257,6 +265,7 @@ export const LogRateAnalysisResults: FC = ({ }, [shouldStart]); useEffect(() => { + setCurrentAnalysisType(analysisType); setCurrentAnalysisWindowParameters(windowParameters); start(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -341,6 +350,40 @@ export const LogRateAnalysisResults: FC = ({ />
+ {showLogRateAnalysisResultsTable && ( + <> + + + {currentAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE + ? i18n.translate('xpack.aiops.analysis.analysisTypeSpikeCallOutTitle', { + defaultMessage: 'Analysis type: Log rate spike', + }) + : i18n.translate('xpack.aiops.analysis.analysisTypeDipCallOutTitle', { + defaultMessage: 'Analysis type: Log rate dip', + })} + + } + color="primary" + iconType="pin" + size="s" + > + + {currentAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE + ? i18n.translate('xpack.aiops.analysis.analysisTypeSpikeCallOutContent', { + defaultMessage: + 'The median log rate in the selected deviation time range is higher than the baseline. Therefore, the analysis results table shows statistically significant items within the deviation time range that are contributors to the spike. The "doc count" column refers to the amount of documents in the deviation time range.', + }) + : i18n.translate('xpack.aiops.analysis.analysisTypeDipCallOutContent', { + defaultMessage: + 'The median log rate in the selected deviation time range is lower than the baseline. Therefore, the analysis results table shows statistically significant items within the baseline time range that are less in number or missing within the deviation time range. The "doc count" column refers to the amount of documents in the baseline time range.', + })} + + + + + )} {errors.length > 0 ? ( <> diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx index 20d4ffefcdbc8..4e87f58293f8b 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import React, { FC, useCallback, useMemo, useState } from 'react'; -import { orderBy } from 'lodash'; +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { orderBy, isEqual } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { @@ -336,6 +336,46 @@ export const LogRateAnalysisResultsTable: FC = }; }, [pageIndex, pageSize, sortField, sortDirection, significantTerms]); + useEffect(() => { + // If no row is hovered or pinned or the user switched to a new page, + // fall back to set the first row into a hovered state to make the + // main document count chart show a comparison view by default. + if ( + (selectedSignificantTerm === null || + !pageOfItems.some((item) => isEqual(item, selectedSignificantTerm))) && + pinnedSignificantTerm === null && + pageOfItems.length > 0 + ) { + setSelectedSignificantTerm(pageOfItems[0]); + } + + // If a user switched pages and a pinned row is no longer visible + // on the current page, set the status of pinned rows back to `null`. + if ( + pinnedSignificantTerm !== null && + !pageOfItems.some((item) => isEqual(item, pinnedSignificantTerm)) + ) { + setPinnedSignificantTerm(null); + } + }, [ + selectedSignificantTerm, + setSelectedSignificantTerm, + setPinnedSignificantTerm, + pageOfItems, + pinnedSignificantTerm, + ]); + + // When the analysis results table unmounts, + // make sure to reset any hovered or pinned rows. + useEffect( + () => () => { + setSelectedSignificantTerm(null); + setPinnedSignificantTerm(null); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + const getRowStyle = (significantTerm: SignificantTerm) => { if ( pinnedSignificantTerm && @@ -393,7 +433,9 @@ export const LogRateAnalysisResultsTable: FC = } }, onMouseEnter: () => { - setSelectedSignificantTerm(significantTerm); + if (pinnedSignificantTerm === null) { + setSelectedSignificantTerm(significantTerm); + } }, onMouseLeave: () => { setSelectedSignificantTerm(null); diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx index 0ab75f9902845..9f45c79b0746d 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import React, { FC, useCallback, useMemo, useState } from 'react'; -import { orderBy } from 'lodash'; +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { orderBy, isEqual } from 'lodash'; import { useEuiBackgroundColor, @@ -423,6 +423,36 @@ export const LogRateAnalysisResultsGroupsTable: FC { + // If no row is hovered or pinned or the user switched to a new page, + // fall back to set the first row into a hovered state to make the + // main document count chart show a comparison view by default. + if ( + (selectedGroup === null || !pageOfItems.some((item) => isEqual(item, selectedGroup))) && + pinnedGroup === null && + pageOfItems.length > 0 + ) { + setSelectedGroup(pageOfItems[0]); + } + + // If a user switched pages and a pinned row is no longer visible + // on the current page, set the status of pinned rows back to `null`. + if (pinnedGroup !== null && !pageOfItems.some((item) => isEqual(item, pinnedGroup))) { + setPinnedGroup(null); + } + }, [selectedGroup, setSelectedGroup, setPinnedGroup, pageOfItems, pinnedGroup]); + + // When the analysis results table unmounts, + // make sure to reset any hovered or pinned rows. + useEffect( + () => () => { + setSelectedGroup(null); + setPinnedGroup(null); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + const getRowStyle = (group: GroupTableItem) => { if (pinnedGroup && pinnedGroup.id === group.id) { return { @@ -464,7 +494,9 @@ export const LogRateAnalysisResultsGroupsTable: FC { - setSelectedGroup(group); + if (pinnedGroup === null) { + setSelectedGroup(group); + } }, onMouseLeave: () => { setSelectedGroup(null); diff --git a/x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx b/x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx new file mode 100644 index 0000000000000..b16f5dde6a406 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx @@ -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 React, { FC, useState } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiForm, + EuiFormRow, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiFieldNumber, + EuiFieldText, + EuiModal, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; +import { FunctionPicker } from '../components/change_point_detection/function_picker'; + +export const MAX_ANOMALY_CHARTS_ALLOWED = 50; + +export const DEFAULT_MAX_SERIES_TO_PLOT = 6; + +export interface AnomalyChartsInitializerProps { + defaultTitle: string; + initialInput?: Partial; + onCreate: (props: { panelTitle: string; maxSeriesToPlot?: number }) => void; + onCancel: () => void; +} + +export const ChangePointChartInitializer: FC = ({ + defaultTitle, + initialInput, + onCreate, + onCancel, +}) => { + const [panelTitle, setPanelTitle] = useState(defaultTitle); + const [maxSeriesToPlot, setMaxSeriesToPlot] = useState( + initialInput?.maxSeriesToPlot ?? DEFAULT_MAX_SERIES_TO_PLOT + ); + + const [fn, setFn] = useState(initialInput?.fn ?? 'avg'); + + const isPanelTitleValid = panelTitle.length > 0; + const isMaxSeriesToPlotValid = + maxSeriesToPlot >= 1 && maxSeriesToPlot <= MAX_ANOMALY_CHARTS_ALLOWED; + const isFormValid = isPanelTitleValid && isMaxSeriesToPlotValid; + + return ( + + + + + + + + + + + } + isInvalid={!isPanelTitleValid} + > + setPanelTitle(e.target.value)} + isInvalid={!isPanelTitleValid} + /> + + + + } + > + + + + + ) : undefined + } + label={ + + } + > + setMaxSeriesToPlot(parseInt(e.target.value, 10))} + min={1} + max={MAX_ANOMALY_CHARTS_ALLOWED} + /> + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/aiops/public/embeddable/const.ts b/x-pack/plugins/aiops/public/embeddable/const.ts new file mode 100644 index 0000000000000..ef655dd25bd7a --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/const.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 MAX_SERIES = 50; diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx new file mode 100644 index 0000000000000..1db2088867497 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { Suspense } from 'react'; +import ReactDOM from 'react-dom'; +import { + Embeddable as AbstractEmbeddable, + EmbeddableInput, + EmbeddableOutput, + IContainer, +} from '@kbn/embeddable-plugin/public'; +import { KibanaThemeProvider, toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import { ThemeServiceStart } from '@kbn/core-theme-browser'; +import { DataPublicPluginStart, UI_SETTINGS } from '@kbn/data-plugin/public'; +import { type CoreStart, IUiSettingsClient } from '@kbn/core/public'; +import { DatePickerContextProvider } from '@kbn/ml-date-picker'; +import { pick } from 'lodash'; +import { LensPublicStart } from '@kbn/lens-plugin/public'; +import { Subject } from 'rxjs'; +import { EmbeddableInputTracker } from './embeddable_chart_component_wrapper'; +import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants'; +import { AiopsAppContext, type AiopsAppDependencies } from '../hooks/use_aiops_app_context'; + +import { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; + +export type EmbeddableChangePointChartInput = EmbeddableInput & EmbeddableChangePointChartProps; + +export type EmbeddableChangePointChartOutput = EmbeddableOutput; + +export interface EmbeddableChangePointChartDeps { + theme: ThemeServiceStart; + data: DataPublicPluginStart; + uiSettings: IUiSettingsClient; + http: CoreStart['http']; + notifications: CoreStart['notifications']; + i18n: CoreStart['i18n']; + lens: LensPublicStart; +} + +export type IEmbeddableChangePointChart = typeof EmbeddableChangePointChart; + +export class EmbeddableChangePointChart extends AbstractEmbeddable< + EmbeddableChangePointChartInput, + EmbeddableChangePointChartOutput +> { + public readonly type = EMBEDDABLE_CHANGE_POINT_CHART_TYPE; + + private reload$ = new Subject(); + + public reload(): void { + this.reload$.next(Date.now()); + } + + private node?: HTMLElement; + + constructor( + private readonly deps: EmbeddableChangePointChartDeps, + initialInput: EmbeddableChangePointChartInput, + parent?: IContainer + ) { + super(initialInput, { defaultTitle: initialInput.title }, parent); + } + + public reportsEmbeddableLoad() { + return true; + } + + public onLoading() { + this.renderComplete.dispatchInProgress(); + this.updateOutput({ loading: true, error: undefined }); + } + + public onError(error: Error) { + this.renderComplete.dispatchError(); + this.updateOutput({ loading: false, error: { name: error.name, message: error.message } }); + } + + public onRenderComplete() { + this.renderComplete.dispatchComplete(); + this.updateOutput({ loading: false, error: undefined }); + } + + render(el: HTMLElement): void { + super.render(el); + + this.node = el; + // required for the export feature to work + this.node.setAttribute('data-shared-item', ''); + + const I18nContext = this.deps.i18n.Context; + + const datePickerDeps = { + ...pick(this.deps, ['data', 'http', 'notifications', 'theme', 'uiSettings']), + toMountPoint, + wrapWithTheme, + uiSettingsKeys: UI_SETTINGS, + }; + + const input = this.getInput(); + const input$ = this.getInput$(); + + ReactDOM.render( + + + + + + + + + + + , + el + ); + } + + public destroy() { + super.destroy(); + if (this.node) { + ReactDOM.unmountComponentAtNode(this.node); + } + } +} diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx new file mode 100644 index 0000000000000..d7d1c84cf9f10 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx @@ -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 type { CoreStart } from '@kbn/core/public'; +import type { TimeRange } from '@kbn/es-query'; +import React from 'react'; +import { + EmbeddableFactory, + EmbeddableOutput, + EmbeddableRoot, + useEmbeddableFactory, +} from '@kbn/embeddable-plugin/public'; +import { EuiLoadingChart } from '@elastic/eui'; +import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants'; +import type { AiopsPluginStartDeps } from '../types'; +import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; + +export interface EmbeddableChangePointChartProps { + dataViewId: string; + timeRange: TimeRange; + fn: 'avg' | 'sum' | 'min' | 'max' | string; + metricField: string; + splitField?: string; + partitions?: string[]; + maxSeriesToPlot?: number; +} + +export function getEmbeddableChangePointChart(core: CoreStart, plugins: AiopsPluginStartDeps) { + const { embeddable: embeddableStart } = plugins; + const factory = embeddableStart.getEmbeddableFactory( + EMBEDDABLE_CHANGE_POINT_CHART_TYPE + )!; + + return (props: EmbeddableChangePointChartProps) => { + const input = { ...props }; + return ( + + ); + }; +} + +function EmbeddableRootWrapper({ + factory, + input, +}: { + factory: EmbeddableFactory; + input: EmbeddableChangePointChartInput; +}) { + const [embeddable, loading, error] = useEmbeddableFactory({ + factory, + input, + }); + if (loading) { + return ; + } + return ; +} diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts new file mode 100644 index 0000000000000..348f50659a932 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EmbeddableFactoryDefinition, + ErrorEmbeddable, + IContainer, +} from '@kbn/embeddable-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { type DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; +import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants'; +import type { AiopsPluginStart, AiopsPluginStartDeps } from '../types'; +import { + EmbeddableChangePointChart, + EmbeddableChangePointChartInput, +} from './embeddable_change_point_chart'; + +export interface EmbeddableChangePointChartStartServices { + data: DataPublicPluginStart; +} + +export type EmbeddableChangePointChartType = typeof EMBEDDABLE_CHANGE_POINT_CHART_TYPE; + +export class EmbeddableChangePointChartFactory implements EmbeddableFactoryDefinition { + public readonly type = EMBEDDABLE_CHANGE_POINT_CHART_TYPE; + + public readonly grouping = [ + { + id: 'ml', + getDisplayName: () => + i18n.translate('xpack.aiops.navMenu.mlAppNameText', { + defaultMessage: 'Machine Learning', + }), + getIconType: () => 'machineLearningApp', + }, + ]; + + constructor( + private readonly getStartServices: StartServicesAccessor + ) {} + + /** + * TODO + */ + public isEditable = async () => { + return false; + }; + + getDisplayName() { + return i18n.translate('xpack.aiops.embeddableChangePointChartDisplayName', { + defaultMessage: 'Change point detection', + }); + } + + /** + * TODO + */ + canCreateNew() { + return false; + } + + public async getExplicitInput(): Promise> { + const [coreStart] = await this.getStartServices(); + + try { + const { resolveEmbeddableChangePointUserInput } = await import('./handle_explicit_input'); + return await resolveEmbeddableChangePointUserInput(coreStart); + } catch (e) { + return Promise.reject(); + } + } + + async create(input: EmbeddableChangePointChartInput, parent?: IContainer) { + try { + const [{ i18n: i18nService, theme, http, uiSettings, notifications }, { lens, data }] = + await this.getStartServices(); + + return new EmbeddableChangePointChart( + { + theme, + http, + i18n: i18nService, + uiSettings, + data, + notifications, + lens, + }, + input, + parent + ); + } catch (e) { + return new ErrorEmbeddable(e, input, parent); + } + } +} diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx new file mode 100644 index 0000000000000..a856aec67e924 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { type Observable } from 'rxjs'; +import React, { FC, useMemo } from 'react'; +import { useTimefilter } from '@kbn/ml-date-picker'; +import { css } from '@emotion/react'; +import useObservable from 'react-use/lib/useObservable'; +import { ReloadContextProvider } from '../hooks/use_reload'; +import type { + ChangePointAnnotation, + ChangePointDetectionRequestParams, +} from '../components/change_point_detection/change_point_detection_context'; +import { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; +import { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; +import { FilterQueryContextProvider, useFilerQueryUpdates } from '../hooks/use_filters_query'; +import { DataSourceContextProvider, useDataSource } from '../hooks/use_data_source'; +import { useAiopsAppContext } from '../hooks/use_aiops_app_context'; +import { useTimeBuckets } from '../hooks/use_time_buckets'; +import { createMergedEsQuery } from '../application/utils/search_utils'; +import { useChangePointResults } from '../components/change_point_detection/use_change_point_agg_request'; +import { ChartsGrid } from '../components/change_point_detection/charts_grid'; +import { NoChangePointsWarning } from '../components/change_point_detection/no_change_points_warning'; + +const defaultSort = { + field: 'p_value' as keyof ChangePointAnnotation, + direction: 'asc', +}; + +export const EmbeddableInputTracker: FC<{ + input$: Observable; + initialInput: EmbeddableChangePointChartInput; + reload$: Observable; +}> = ({ input$, initialInput, reload$ }) => { + const input = useObservable(input$, initialInput); + + return ( + + + + + + + + ); +}; + +/** + * Grid component wrapper for embeddable. + * + * @param timeRange + * @param fn + * @param metricField + * @param maxSeriesToPlot + * @param splitField + * @param partitions + * @constructor + */ +export const ChartGridEmbeddableWrapper: FC = ({ + fn, + metricField, + maxSeriesToPlot, + splitField, + partitions, +}) => { + const { filters, query, timeRange } = useFilerQueryUpdates(); + + const fieldConfig = useMemo(() => { + return { fn, metricField, splitField }; + }, [fn, metricField, splitField]); + + const { dataView } = useDataSource(); + const { uiSettings } = useAiopsAppContext(); + const timeBuckets = useTimeBuckets(); + const timefilter = useTimefilter(); + + const interval = useMemo(() => { + timeBuckets.setInterval('auto'); + timeBuckets.setBounds(timefilter.calculateBounds(timeRange)); + return timeBuckets.getInterval().expression; + }, [timeRange, timeBuckets, timefilter]); + + const combinedQuery = useMemo(() => { + const mergedQuery = createMergedEsQuery(query, filters, dataView, uiSettings); + if (!Array.isArray(mergedQuery.bool?.filter)) { + if (!mergedQuery.bool) { + mergedQuery.bool = {}; + } + mergedQuery.bool.filter = []; + } + + mergedQuery.bool!.filter.push({ + range: { + [dataView.timeFieldName!]: { + from: timeRange.from, + to: timeRange.to, + }, + }, + }); + + if (partitions && fieldConfig.splitField) { + mergedQuery.bool?.filter.push({ + terms: { + [fieldConfig.splitField]: partitions, + }, + }); + } + + return mergedQuery; + }, [ + dataView, + fieldConfig.splitField, + filters, + partitions, + query, + timeRange.from, + timeRange.to, + uiSettings, + ]); + + const requestParams = useMemo(() => { + return { interval } as ChangePointDetectionRequestParams; + }, [interval]); + + const { results } = useChangePointResults(fieldConfig, requestParams, combinedQuery, 10000); + + const changePoints = useMemo(() => { + let resultChangePoints: ChangePointAnnotation[] = results.sort((a, b) => { + if (defaultSort.direction === 'asc') { + return (a[defaultSort.field] as number) - (b[defaultSort.field] as number); + } else { + return (b[defaultSort.field] as number) - (a[defaultSort.field] as number); + } + }); + + if (maxSeriesToPlot) { + resultChangePoints = resultChangePoints.slice(0, maxSeriesToPlot); + } + + return resultChangePoints; + }, [results, maxSeriesToPlot]); + + return ( +
+ {changePoints.length > 0 ? ( + ({ ...r, ...fieldConfig }))} + interval={requestParams.interval} + /> + ) : ( + + )} +
+ ); +}; diff --git a/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx b/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx new file mode 100644 index 0000000000000..20027c10cd22e --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreStart } from '@kbn/core/public'; +import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import React from 'react'; +import { ChangePointChartInitializer } from './change_point_chart_initializer'; +import { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; + +export async function resolveEmbeddableChangePointUserInput( + coreStart: CoreStart, + input?: EmbeddableChangePointChartInput +): Promise> { + const { overlays } = coreStart; + + return new Promise(async (resolve, reject) => { + try { + const title = input?.title; + const { theme$ } = coreStart.theme; + const modalSession = overlays.openModal( + toMountPoint( + wrapWithTheme( + { + modalSession.close(); + resolve({ + title: panelTitle, + maxSeriesToPlot, + }); + }} + onCancel={() => { + modalSession.close(); + reject(); + }} + />, + theme$ + ) + ) + ); + } catch (error) { + reject(error); + } + }); +} diff --git a/x-pack/plugins/aiops/public/embeddable/index.ts b/x-pack/plugins/aiops/public/embeddable/index.ts new file mode 100644 index 0000000000000..d742a1321c101 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/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 { EmbeddableChangePointChartFactory } from './embeddable_change_point_chart_factory'; +export { type EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; diff --git a/x-pack/plugins/aiops/public/embeddable/register_embeddable.ts b/x-pack/plugins/aiops/public/embeddable/register_embeddable.ts new file mode 100644 index 0000000000000..e3a3f31e757a0 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddable/register_embeddable.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreSetup } from '@kbn/core-lifecycle-browser'; +import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; +import type { AiopsPluginStart, AiopsPluginStartDeps } from '../types'; +import { EmbeddableChangePointChartFactory } from './embeddable_change_point_chart_factory'; + +export const registerEmbeddable = ( + core: CoreSetup, + embeddable: EmbeddableSetup +) => { + const factory = new EmbeddableChangePointChartFactory(core.getStartServices); + embeddable.registerEmbeddableFactory(factory.type, factory); +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts index 90592e20e1e79..138f5277ce7f9 100644 --- a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts +++ b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts @@ -29,6 +29,9 @@ import type { FieldStatsServices, } from '@kbn/unified-field-list/src/components/field_stats'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import type { CasesUiStart } from '@kbn/cases-plugin/public'; /** * AIOps App Dependencies to be provided via React context. @@ -104,6 +107,9 @@ export interface AiopsAppDependencies { dslQuery?: FieldStatsProps['dslQuery']; }>; }; + presentationUtil?: PresentationUtilPluginStart; + embeddable?: EmbeddableStart; + cases?: CasesUiStart; } /** diff --git a/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts b/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts new file mode 100644 index 0000000000000..b7b64b5f9cd60 --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_cases_modal.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 { useCallback } from 'react'; +import { stringHash } from '@kbn/ml-string-hash'; +import { AttachmentType } from '@kbn/cases-plugin/common'; +import { EmbeddableChangePointChartInput } from '../embeddable/embeddable_change_point_chart'; +import { EmbeddableChangePointChartType } from '../embeddable/embeddable_change_point_chart_factory'; +import { useAiopsAppContext } from './use_aiops_app_context'; + +/** + * Returns a callback for opening the cases modal with provided attachment state. + */ +export const useCasesModal = ( + embeddableType: EmbeddableType +) => { + const { cases } = useAiopsAppContext(); + + const selectCaseModal = cases?.hooks.useCasesAddToExistingCaseModal(); + + return useCallback( + (persistableState: Partial>) => { + const persistableStateAttachmentState = { + ...persistableState, + // Creates unique id based on the input + id: stringHash(JSON.stringify(persistableState)).toString(), + }; + + if (!selectCaseModal) { + throw new Error('Cases modal is not available'); + } + + selectCaseModal.open({ + getAttachments: () => [ + { + type: AttachmentType.persistableState, + persistableStateAttachmentTypeId: embeddableType, + persistableStateAttachmentState: JSON.parse( + JSON.stringify(persistableStateAttachmentState) + ), + }, + ], + }); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [embeddableType] + ); +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_data_source.ts b/x-pack/plugins/aiops/public/hooks/use_data_source.ts deleted file mode 100644 index 64cf4fe8ec521..0000000000000 --- a/x-pack/plugins/aiops/public/hooks/use_data_source.ts +++ /dev/null @@ -1,24 +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 { createContext, useContext } from 'react'; -import { DataView } from '@kbn/data-views-plugin/common'; -import type { SavedSearch } from '@kbn/saved-search-plugin/public'; - -export const DataSourceContext = createContext<{ - dataView: DataView | never; - savedSearch: SavedSearch | null; -}>({ - get dataView(): never { - throw new Error('DataSourceContext is not implemented'); - }, - savedSearch: null, -}); - -export function useDataSource() { - return useContext(DataSourceContext); -} diff --git a/x-pack/plugins/aiops/public/hooks/use_data_source.tsx b/x-pack/plugins/aiops/public/hooks/use_data_source.tsx new file mode 100644 index 0000000000000..68502f8e2648e --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_data_source.tsx @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, FC, useCallback, useContext, useEffect, useState } from 'react'; +import { DataView } from '@kbn/data-views-plugin/common'; +import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useAiopsAppContext } from './use_aiops_app_context'; + +export const DataSourceContext = createContext({ + get dataView(): never { + throw new Error('DataSourceContext is not implemented'); + }, + savedSearch: null, +}); + +export function useDataSource() { + return useContext(DataSourceContext); +} + +export interface DataViewAndSavedSearch { + savedSearch: SavedSearch | null; + dataView: DataView; +} + +/** + * Context provider that resolves current data view and the saved search + * + * @param children + * @constructor + */ +export const DataSourceContextProvider: FC<{ dataViewId?: string; savedSearchId?: string }> = ({ + dataViewId, + savedSearchId, + children, +}) => { + const [value, setValue] = useState(); + const [error, setError] = useState(); + + const { + data: { dataViews }, + // uiSettings, + // savedSearch: savedSearchService, + } = useAiopsAppContext(); + + /** + * Resolve data view or saved search if exists. + */ + const resolveDataSource = useCallback(async (): Promise => { + const dataViewAndSavedSearch: DataViewAndSavedSearch = { + savedSearch: null, + // @ts-ignore + dataView: null, + }; + + // support only data views for now + if (dataViewId !== undefined) { + dataViewAndSavedSearch.dataView = await dataViews.get(dataViewId); + } + + const { savedSearch, dataView } = dataViewAndSavedSearch; + + return { + dataView, + savedSearch, + }; + }, [dataViewId, dataViews]); + + useEffect(() => { + resolveDataSource() + .then((result) => { + setValue(result); + }) + .catch((e) => { + setError(e); + }); + }, [resolveDataSource]); + + if (!value && !error) return null; + + if (error) { + return ( + + + + } + body={

{error.message}

} + /> + ); + } + + return {children}; +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_filters_query.tsx b/x-pack/plugins/aiops/public/hooks/use_filters_query.tsx new file mode 100644 index 0000000000000..d3554985eabd5 --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_filters_query.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { type FC, createContext, useEffect, useState, useContext } from 'react'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { useTimeRangeUpdates } from '@kbn/ml-date-picker'; +import { type AggregateQuery } from '@kbn/es-query'; +import { useAiopsAppContext } from './use_aiops_app_context'; + +export const FilterQueryContext = createContext<{ + filters: Filter[]; + query: Query; + timeRange: TimeRange; +}>({ + get filters(): Filter[] { + throw new Error('FilterQueryContext is not initialized'); + }, + get query(): Query { + throw new Error('FilterQueryContext is not initialized'); + }, + get timeRange(): TimeRange { + throw new Error('FilterQueryContext is not initialized'); + }, +}); + +/** + * Helper context to provide the latest filter, query and time range values + * from the data plugin. + * Also merges custom filters and queries provided with an input. + * + * @param children + * @constructor + */ +export const FilterQueryContextProvider: FC<{ timeRange?: TimeRange }> = ({ + children, + timeRange, +}) => { + const { + data: { + query: { filterManager, queryString }, + }, + } = useAiopsAppContext(); + + const [resultFilters, setResultFilter] = useState(filterManager.getFilters()); + const [resultQuery, setResultQuery] = useState(queryString.getQuery()); + + const timeRangeUpdates = useTimeRangeUpdates(true); + + useEffect(() => { + const sub = filterManager.getUpdates$().subscribe(() => { + setResultFilter(filterManager.getFilters()); + }); + return () => { + sub.unsubscribe(); + }; + }, [filterManager]); + + useEffect(() => { + const sub = queryString.getUpdates$().subscribe(() => { + setResultQuery(queryString.getQuery()); + }); + return () => { + sub.unsubscribe(); + }; + }, [queryString]); + + return ( + + {children} + + ); +}; + +export const useFilerQueryUpdates = () => { + return useContext(FilterQueryContext); +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_reload.tsx b/x-pack/plugins/aiops/public/hooks/use_reload.tsx new file mode 100644 index 0000000000000..42c3d95e848fb --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_reload.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useContext } from 'react'; +import { type Observable } from 'rxjs'; +import useObservable from 'react-use/lib/useObservable'; + +export interface ReloadContextValue { + refreshTimestamp: number; +} + +export const ReloadContext = React.createContext({ + refreshTimestamp: Date.now(), +}); + +export const ReloadContextProvider: React.FC<{ reload$: Observable }> = ({ + reload$, + children, +}) => { + const refreshTimestamp = useObservable(reload$, Date.now()); + return {children}; +}; + +export const useReload = () => { + return useContext(ReloadContext); +}; diff --git a/x-pack/plugins/aiops/public/index.ts b/x-pack/plugins/aiops/public/index.ts index 4c7ec4e9fc100..3e6ea42afe55c 100755 --- a/x-pack/plugins/aiops/public/index.ts +++ b/x-pack/plugins/aiops/public/index.ts @@ -13,8 +13,6 @@ export function plugin() { return new AiopsPlugin(); } -export { LOG_RATE_ANALYSIS_TYPE, type LogRateAnalysisType } from '../common/constants'; - export type { AiopsAppDependencies } from './hooks/use_aiops_app_context'; export type { LogRateAnalysisAppStateProps } from './components/log_rate_analysis'; export type { LogRateAnalysisContentWrapperProps } from './components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper'; diff --git a/x-pack/plugins/aiops/public/plugin.ts b/x-pack/plugins/aiops/public/plugin.tsx similarity index 54% rename from x-pack/plugins/aiops/public/plugin.ts rename to x-pack/plugins/aiops/public/plugin.tsx index 51a109d8c90f9..0023d2f2baa9e 100755 --- a/x-pack/plugins/aiops/public/plugin.ts +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -6,23 +6,42 @@ */ import type { CoreStart, Plugin } from '@kbn/core/public'; +import { type CoreSetup } from '@kbn/core/public'; import { firstValueFrom } from 'rxjs'; - -import { +import type { AiopsPluginSetup, AiopsPluginSetupDeps, AiopsPluginStart, AiopsPluginStartDeps, } from './types'; +import { getEmbeddableChangePointChart } from './embeddable/embeddable_change_point_chart_component'; export class AiopsPlugin implements Plugin { - public setup() { - return {}; + public setup( + core: CoreSetup, + { embeddable, cases, licensing }: AiopsPluginSetupDeps + ) { + firstValueFrom(licensing.license$).then(async (license) => { + if (license.hasAtLeast('platinum')) { + if (embeddable) { + const { registerEmbeddable } = await import('./embeddable/register_embeddable'); + registerEmbeddable(core, embeddable); + } + + if (cases) { + const [coreStart, pluginStart] = await core.getStartServices(); + const { registerChangePointChartsAttachment } = await import( + './cases/register_change_point_charts_attachment' + ); + registerChangePointChartsAttachment(cases, coreStart, pluginStart); + } + } + }); } - public start(core: CoreStart, plugins: AiopsPluginStartDeps) { + public start(core: CoreStart, plugins: AiopsPluginStartDeps): AiopsPluginStart { // importing async to keep the aiops plugin size to a minimum Promise.all([ import('@kbn/ui-actions-plugin/public'), @@ -42,7 +61,9 @@ export class AiopsPlugin } }); - return {}; + return { + EmbeddableChangePointChart: getEmbeddableChangePointChart(core, plugins), + }; } public stop() {} diff --git a/x-pack/plugins/aiops/public/types.ts b/x-pack/plugins/aiops/public/types.ts index 8441e36529f90..10cec5ae49df8 100755 --- a/x-pack/plugins/aiops/public/types.ts +++ b/x-pack/plugins/aiops/public/types.ts @@ -15,9 +15,16 @@ import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import type { CasesUiSetup } from '@kbn/cases-plugin/public'; +import { LicensingPluginSetup } from '@kbn/licensing-plugin/public'; +import type { EmbeddableChangePointChartProps } from './embeddable'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AiopsPluginSetupDeps {} +export interface AiopsPluginSetupDeps { + embeddable: EmbeddableSetup; + cases: CasesUiSetup; + licensing: LicensingPluginSetup; +} export interface AiopsPluginStartDeps { data: DataPublicPluginStart; @@ -30,12 +37,10 @@ export interface AiopsPluginStartDeps { storage: IStorageWrapper; licensing: LicensingPluginStart; executionContext: ExecutionContextStart; + embeddable: EmbeddableStart; } -/** - * aiops plugin server setup contract - */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AiopsPluginSetup {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AiopsPluginStart {} +export type AiopsPluginSetup = void; +export interface AiopsPluginStart { + EmbeddableChangePointChart: React.ComponentType; +} diff --git a/x-pack/plugins/aiops/server/plugin.ts b/x-pack/plugins/aiops/server/plugin.ts index b18f728a06a35..356dfc570e6ef 100755 --- a/x-pack/plugins/aiops/server/plugin.ts +++ b/x-pack/plugins/aiops/server/plugin.ts @@ -10,6 +10,7 @@ import { Subscription } from 'rxjs'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server'; import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; +import { CASES_ATTACHMENT_CHANGE_POINT_CHART } from '../common/constants'; import { isActiveLicense } from './lib/license'; import { AiopsLicense, @@ -54,6 +55,12 @@ export class AiopsPlugin defineLogCategorizationRoutes(router, aiopsLicense); }); + if (plugins.cases) { + plugins.cases.attachmentFramework.registerPersistableState({ + id: CASES_ATTACHMENT_CHANGE_POINT_CHART, + }); + } + return {}; } diff --git a/x-pack/plugins/aiops/server/types.ts b/x-pack/plugins/aiops/server/types.ts index 8678fe186d56c..8359996138dce 100755 --- a/x-pack/plugins/aiops/server/types.ts +++ b/x-pack/plugins/aiops/server/types.ts @@ -7,10 +7,12 @@ import type { PluginSetup, PluginStart } from '@kbn/data-plugin/server'; import type { LicensingPluginStart } from '@kbn/licensing-plugin/server'; +import type { CasesSetup } from '@kbn/cases-plugin/server'; export interface AiopsPluginSetupDeps { data: PluginSetup; licensing: LicensingPluginStart; + cases?: CasesSetup; } export interface AiopsPluginStartDeps { diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index 685f549db4d3c..dd9dc07a3b33a 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -57,6 +57,11 @@ "@kbn/unified-field-list", "@kbn/unified-search-plugin", "@kbn/utility-types", + "@kbn/presentation-util-plugin", + "@kbn/embeddable-plugin", + "@kbn/core-theme-browser", + "@kbn/core-lifecycle-browser", + "@kbn/cases-plugin", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.json b/x-pack/plugins/alerting/docs/openapi/bundled.json index 104b044875c64..31b043f812d47 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.json +++ b/x-pack/plugins/alerting/docs/openapi/bundled.json @@ -2616,14 +2616,15 @@ "actions": { "type": "array", "default": [], - "required": [ - "group", - "id", - "params" - ], "nullable": true, "items": { "type": "object", + "required": [ + "group", + "id", + "params" + ], + "description": "An action that runs under defined conditions.\n", "properties": { "alerts_filter": { "type": "object", @@ -2696,7 +2697,7 @@ }, "frequency": { "type": "object", - "description": "The parameters that affect how often actions are generated. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level.\n", + "description": "The properties that affect how often actions are generated. If the rule type supports setting `summary` to `true`, the action can be a summary of alerts at the specified notification interval. Otherwise, an action runs for each alert at the specified notification interval. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level.\n", "required": [ "notify_when", "summary" @@ -2716,7 +2717,7 @@ }, "group": { "type": "string", - "description": "The group name for the actions. If you don't need to group actions, set to `default`.", + "description": "The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names. If you don't need to group actions, set to `default`.\n", "example": "default" }, "id": { diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.yaml b/x-pack/plugins/alerting/docs/openapi/bundled.yaml index d0f2cccf0a9cc..72c8cf2da3828 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.yaml +++ b/x-pack/plugins/alerting/docs/openapi/bundled.yaml @@ -1656,13 +1656,15 @@ components: actions: type: array default: [] - required: - - group - - id - - params nullable: true items: type: object + required: + - group + - id + - params + description: | + An action that runs under defined conditions. properties: alerts_filter: type: object @@ -1721,7 +1723,7 @@ components: frequency: type: object description: | - The parameters that affect how often actions are generated. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level. + The properties that affect how often actions are generated. If the rule type supports setting `summary` to `true`, the action can be a summary of alerts at the specified notification interval. Otherwise, an action runs for each alert at the specified notification interval. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level. required: - notify_when - summary @@ -1735,7 +1737,8 @@ components: $ref: '#/components/schemas/throttle' group: type: string - description: The group name for the actions. If you don't need to group actions, set to `default`. + description: | + The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names. If you don't need to group actions, set to `default`. example: default id: type: string diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml index e2b959fb3cf53..ee11bead01350 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml @@ -1,12 +1,14 @@ type: array default: [] -required: - - group - - id - - params nullable: true items: type: object + required: + - group + - id + - params + description: > + An action that runs under defined conditions. properties: alerts_filter: type: object @@ -64,7 +66,9 @@ items: frequency: type: object description: > - The parameters that affect how often actions are generated. + The properties that affect how often actions are generated. + If the rule type supports setting `summary` to `true`, the action can be a summary of alerts at the specified notification interval. + Otherwise, an action runs for each alert at the specified notification interval. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level. required: - notify_when @@ -79,7 +83,10 @@ items: $ref: 'throttle.yaml' group: type: string - description: The group name for the actions. If you don't need to group actions, set to `default`. + description: > + The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). + Each rule type has a list of valid action group names. + If you don't need to group actions, set to `default`. example: default id: type: string diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts index e3942b26ee6fa..5c4acce28b108 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts @@ -114,6 +114,7 @@ const getIndexTemplatePutBody = (opts?: GetIndexTemplatePutBodyOpts) => { name: '.alerts-ilm-policy', rollover_alias: `.alerts-${context ? context : 'test'}.alerts-${namespace}`, }, + 'index.mapping.ignore_malformed': true, 'index.mapping.total_fields.limit': 2500, }, mappings: { @@ -640,6 +641,7 @@ describe('Alerts Service', () => { name: '.alerts-ilm-policy', rollover_alias: `.alerts-empty.alerts-default`, }, + 'index.mapping.ignore_malformed': true, 'index.mapping.total_fields.limit': 2500, }, mappings: { diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts index d4ce203a0d0e3..38c2207e5f410 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts @@ -42,6 +42,7 @@ const IndexTemplate = (namespace: string = 'default') => ({ name: 'test-ilm-policy', rollover_alias: `.alerts-test.alerts-${namespace}`, }, + 'index.mapping.ignore_malformed': true, 'index.mapping.total_fields.limit': 2500, }, }, diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts index a17fad2d875ed..388fe6344a51f 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts @@ -54,6 +54,7 @@ export const getIndexTemplate = ({ rollover_alias: indexPatterns.alias, }, 'index.mapping.total_fields.limit': totalFieldsLimit, + 'index.mapping.ignore_malformed': true, }, mappings: { dynamic: false, diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts index dd73051be9f2e..6886fc582f631 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts @@ -151,4 +151,30 @@ describe('Transaction details', () => { }); }); }); + + describe('when changing filters which results in no trace samples', () => { + it('trace waterfall must reset to empty state', () => { + cy.visitKibana( + `/app/apm/services/opbeans-java/transactions/view?${new URLSearchParams( + { + ...timeRange, + transactionName: 'GET /api/product', + } + )}` + ); + + cy.getByTestSubj('apmWaterfallButton').should('exist'); + + cy.getByTestSubj('apmUnifiedSearchBar') + .type(`_id: "123"`) + .type('{enter}'); + + cy.getByTestSubj('apmWaterfallButton').should('not.exist'); + cy.getByTestSubj('apmNoTraceFound').should('exist'); + + cy.reload(); + + cy.getByTestSubj('apmNoTraceFound').should('exist'); + }); + }); }); diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx index 5137713457d5c..806ca0e59684b 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx @@ -54,27 +54,27 @@ export function ErrorSampleContextualInsight({ role: MessageRole.User, content: `I'm an SRE. I am looking at an exception and trying to understand what it means. - Your task is to describe what the error means and what it could be caused by. - - The error occurred on a service called ${serviceName}, which is a ${runtimeName} service written in ${languageName}. The - runtime version is ${runtimeVersion}. - - The request it occurred for is called ${transactionName}. - - ${ - logStacktrace - ? `The log stacktrace: - ${logStacktrace}` - : '' - } - - ${ - exceptionStacktrace - ? `The exception stacktrace: - ${exceptionStacktrace}` - : '' - } - `, +Your task is to describe what the error means and what it could be caused by. + +The error occurred on a service called ${serviceName}, which is a ${runtimeName} service written in ${languageName}. The +runtime version is ${runtimeVersion}. + +The request it occurred for is called ${transactionName}. + +${ + logStacktrace + ? `The log stacktrace: +${logStacktrace}` + : '' +} + +${ + exceptionStacktrace + ? `The exception stacktrace: +${exceptionStacktrace}` + : '' +} +`, }, }, ]; 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 index 084f11a85f621..7b3eff0deb747 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx @@ -19,18 +19,26 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { isEmpty } from 'lodash'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { IndexLifecyclePhaseSelect } from './index_lifecycle_phase_select'; import { ServicesTable } from './services_table'; import { SearchBar } from '../../shared/search_bar/search_bar'; import { StorageChart } from './storage_chart'; import { PermissionDenied } from './prompts/permission_denied'; -import { useFetcher, FETCH_STATUS } from '../../../hooks/use_fetcher'; +import { + useFetcher, + FETCH_STATUS, + isPending, +} from '../../../hooks/use_fetcher'; import { SummaryStats } from './summary_stats'; import { ApmEnvironmentFilter } from '../../shared/environment_filter'; import { TipsAndResources } from './resources/tips_and_resources'; import { useLocalStorage } from '../../../hooks/use_local_storage'; import { getKibanaAdvancedSettingsHref } from './get_storage_explorer_links'; +import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useTimeRange } from '../../../hooks/use_time_range'; type CalloutType = 'crossClusterSearch' | 'optimizePerformance'; @@ -48,6 +56,11 @@ const dismissButtonText = i18n.translate( export function StorageExplorer() { const { core } = useApmPluginContext(); + const { + query: { rangeFrom, rangeTo, environment, kuery, indexLifecyclePhase }, + } = useApmParams('/storage-explorer'); + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); const [calloutDismissed, setCalloutDismissed] = useLocalStorage( 'apm.storageExplorer.calloutDismissed', @@ -72,6 +85,28 @@ export function StorageExplorer() { [calloutDismissed] ); + const { data: summaryStatsData, status: summaryStatsStatus } = + useProgressiveFetcher( + (callApmApi) => { + return callApmApi('GET /internal/apm/storage_explorer_summary_stats', { + params: { + query: { + indexLifecyclePhase, + environment, + kuery, + start, + end, + }, + }, + }); + }, + [indexLifecyclePhase, environment, kuery, start, end] + ); + + const loadingSummaryStats = isPending(summaryStatsStatus); + + const hasSummaryStatsData = !isEmpty(summaryStatsData); + const loading = hasPrivilegesStatus === FETCH_STATUS.LOADING; if (loading) { @@ -190,12 +225,19 @@ export function StorageExplorer() { )} - + - + 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 index b9ed2e6730275..2faad289151e4 100644 --- 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 @@ -28,7 +28,10 @@ import { isEmpty } from 'lodash'; import { downloadJson } from '../../../../utils/download_json'; import { AgentName } from '../../../../../typings/es_schemas/ui/fields/agent'; import { EnvironmentBadge } from '../../../shared/environment_badge'; -import { asPercent } from '../../../../../common/utils/formatters'; +import { + asPercent, + asTransactionRate, +} from '../../../../../common/utils/formatters'; import { ServiceLink } from '../../../shared/links/apm/service_link'; import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { StorageDetailsPerService } from './storage_details_per_service'; @@ -42,6 +45,7 @@ import { useProgressiveFetcher } from '../../../../hooks/use_progressive_fetcher import { useTimeRange } from '../../../../hooks/use_time_range'; import { SizeLabel } from './size_label'; import { joinByKey } from '../../../../../common/utils/join_by_key'; +import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; interface StorageExplorerItem { serviceName: string; @@ -57,8 +61,15 @@ enum StorageExplorerFieldName { Sampling = 'sampling', Size = 'size', } +interface Props { + summaryStatsData?: APIReturnType<'GET /internal/apm/storage_explorer_summary_stats'>; + loadingSummaryStats: boolean; +} -export function ServicesTable() { +export function ServicesTable({ + summaryStatsData, + loadingSummaryStats, +}: Props) { const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState< Record >({}); @@ -283,6 +294,9 @@ export function ServicesTable() { }, ]; + const isDownloadButtonDisable = + isEmpty(serviceStatisticsItems) || loadingSummaryStats; + return ( downloadJson({ fileName: `storage-explorefpr-${moment(Date.now()).format( @@ -309,6 +323,25 @@ export function ServicesTable() { kuery, indexLifecyclePhase, }, + summary: { + totalSize: asDynamicBytes(summaryStatsData?.totalSize), + diskSpaceUsedPct: asPercent( + summaryStatsData?.diskSpaceUsedPct, + 1 + ), + estimatedIncrementalSize: asDynamicBytes( + summaryStatsData?.estimatedIncrementalSize + ), + dailyDataGeneration: asDynamicBytes( + summaryStatsData?.dailyDataGeneration + ), + tracesPerMinute: asTransactionRate( + summaryStatsData?.tracesPerMinute + ), + numberOfServices: ( + summaryStatsData?.numberOfServices ?? 0 + ).toString(), + }, services: serviceStatisticsItems.map((item) => ({ ...item, sampling: asPercent(item?.sampling, 1), diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx index 0021af524b3b7..57a348d4839d4 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx @@ -22,34 +22,29 @@ import { } from '@elastic/eui'; import { useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; -import { isEmpty } from 'lodash'; -import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher'; -import { useTimeRange } from '../../../hooks/use_time_range'; import { useApmParams } from '../../../hooks/use_apm_params'; import { asDynamicBytes, asPercent } from '../../../../common/utils/formatters'; import { useApmRouter } from '../../../hooks/use_apm_router'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; -import { isPending } from '../../../hooks/use_fetcher'; + import { asTransactionRate } from '../../../../common/utils/formatters'; import { getIndexManagementHref } from './get_storage_explorer_links'; +import { APIReturnType } from '../../../services/rest/create_call_apm_api'; + +interface Props { + data?: APIReturnType<'GET /internal/apm/storage_explorer_summary_stats'>; + loading: boolean; + hasData: boolean; +} -export function SummaryStats() { +export function SummaryStats({ data, loading, hasData }: Props) { const router = useApmRouter(); const { core } = useApmPluginContext(); const { - query: { - rangeFrom, - rangeTo, - environment, - kuery, - indexLifecyclePhase, - comparisonEnabled, - }, + query: { rangeFrom, rangeTo, environment, kuery, comparisonEnabled }, } = useApmParams('/storage-explorer'); - const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const serviceInventoryLink = router.link('/services', { query: { rangeFrom, @@ -61,27 +56,6 @@ export function SummaryStats() { }, }); - const { data, status } = useProgressiveFetcher( - (callApmApi) => { - return callApmApi('GET /internal/apm/storage_explorer_summary_stats', { - params: { - query: { - indexLifecyclePhase, - environment, - kuery, - start, - end, - }, - }, - }); - }, - [indexLifecyclePhase, environment, kuery, start, end] - ); - - const loading = isPending(status); - - const hasData = !isEmpty(data); - return ( +
- +
); } diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts b/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts index 81e0d736aee19..a01bb00ea0e44 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts @@ -56,7 +56,10 @@ export function useWaterfallFetcher({ [traceId, start, end, transactionId] ); - const waterfall = useMemo(() => getWaterfall(data), [data]); + const waterfall = useMemo( + () => getWaterfall(traceId ? data : INITIAL_DATA), + [data, traceId] + ); return { waterfall, status, error }; } diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx index e19400490133b..a14572f089137 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx @@ -60,8 +60,10 @@ export function WaterfallWithSummary({ const isLoading = waterfallFetchResult.status === FETCH_STATUS.LOADING || traceSamplesFetchStatus === FETCH_STATUS.LOADING; + // When traceId is not present, call to waterfallFetchResult will not be initiated const isSucceded = - waterfallFetchResult.status === FETCH_STATUS.SUCCESS && + (waterfallFetchResult.status === FETCH_STATUS.SUCCESS || + waterfallFetchResult.status === FETCH_STATUS.NOT_INITIATED) && traceSamplesFetchStatus === FETCH_STATUS.SUCCESS; useEffect(() => { @@ -96,6 +98,7 @@ export function WaterfallWithSummary({ })}
} + data-test-subj="apmNoTraceFound" titleSize="s" /> ); diff --git a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx index ca4fb2755f341..bd0e4f242cf39 100644 --- a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -171,7 +171,7 @@ export function TransactionsTable({ start, end, transactionType, - useDurationSummary: shouldUseDurationSummary, + useDurationSummary: !!shouldUseDurationSummary, latencyAggregationType: latencyAggregationType as LatencyAggregationType, documentType: preferred.source.documentType, @@ -256,7 +256,7 @@ export function TransactionsTable({ transactionType, documentType: preferred.source.documentType, rollupInterval: preferred.source.rollupInterval, - useDurationSummary: shouldUseDurationSummary, + useDurationSummary: !!shouldUseDurationSummary, latencyAggregationType: latencyAggregationType as LatencyAggregationType, transactionNames: JSON.stringify( diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts index d1d69c60a5720..79266dbafb39d 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts @@ -78,7 +78,7 @@ export function useTransactionLatencyChartsFetcher({ start, end, transactionType, - useDurationSummary: shouldUseDurationSummary, + useDurationSummary: !!shouldUseDurationSummary, transactionName: transactionName || undefined, latencyAggregationType, offset: diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index c99bfd21c9ad1..51c6cdc54131d 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -9,7 +9,7 @@ import React, { FC } from 'react'; import useObservable from 'react-use/lib/useObservable'; import ReactDOM from 'react-dom'; import { CoreStart } from '@kbn/core/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { IEmbeddable, EmbeddableFactory, @@ -62,7 +62,7 @@ const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => { style={{ width: '100%', height: '100%', cursor: 'auto' }} > - + diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/index.tsx index 12e1948260a96..2a875ff88f413 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/index.tsx @@ -7,7 +7,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { StartInitializer } from '../../../plugin'; import { RendererFactory } from '../../../../types'; import { AdvancedFilter } from './component'; @@ -24,7 +24,7 @@ export const advancedFilterFactory: StartInitializer> = height: 50, render(domNode, _, handlers) { ReactDOM.render( - + handlers.event({ name: 'applyFilterAction', data: filter })} value={handlers.getFilter()} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/index.tsx index 76323793ab698..50f534a658359 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/index.tsx @@ -9,7 +9,7 @@ import { fromExpression, toExpression, Ast } from '@kbn/interpreter'; import { get } from 'lodash'; import React from 'react'; import ReactDOM from 'react-dom'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { syncFilterExpression } from '../../../../public/lib/sync_filter_expression'; import { RendererFactory } from '../../../../types'; import { StartInitializer } from '../../../plugin'; @@ -97,7 +97,7 @@ export const dropdownFilterFactory: StartInitializer> = ); ReactDOM.render( - {filter}, + {filter}, domNode, () => handlers.done() ); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/index.tsx index 9ae72a82c8870..52ae1e28f7904 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/index.tsx @@ -9,7 +9,7 @@ import ReactDOM from 'react-dom'; import React from 'react'; import { toExpression } from '@kbn/interpreter'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { syncFilterExpression } from '../../../../public/lib/sync_filter_expression'; import { RendererStrings } from '../../../../i18n'; import { TimeFilter } from './components'; @@ -60,7 +60,7 @@ export const timeFilterFactory: StartInitializer> = ( } ReactDOM.render( - + handlers.event({ name: 'applyFilterAction', data: filter })} filter={filterExpression} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx index 7566db85427ac..fd1e2415d0589 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/markdown/index.tsx @@ -9,7 +9,7 @@ import React, { CSSProperties } from 'react'; import ReactDOM from 'react-dom'; import { CoreTheme } from '@kbn/core/public'; import { Observable } from 'rxjs'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { defaultTheme$ } from '@kbn/presentation-util-plugin/common'; import { Markdown } from '@kbn/kibana-react-plugin/public'; import { StartInitializer } from '../../plugin'; @@ -30,7 +30,7 @@ export const getMarkdownRenderer = const fontStyle = config.font ? config.font.spec : {}; ReactDOM.render( - + +
+
{textString}
, domNode, diff --git a/x-pack/plugins/canvas/kibana.jsonc b/x-pack/plugins/canvas/kibana.jsonc index 05d86386d341f..7e4d0fcff071d 100644 --- a/x-pack/plugins/canvas/kibana.jsonc +++ b/x-pack/plugins/canvas/kibana.jsonc @@ -30,7 +30,7 @@ "visualizations", "uiActions", "share", - "savedObjectsManagement", + "contentManagement", "savedObjectsFinder" ], "optionalPlugins": [ diff --git a/x-pack/plugins/canvas/public/application.tsx b/x-pack/plugins/canvas/public/application.tsx index cc365451b46f0..fa544095a8598 100644 --- a/x-pack/plugins/canvas/public/application.tsx +++ b/x-pack/plugins/canvas/public/application.tsx @@ -17,7 +17,8 @@ import { includes, remove } from 'lodash'; import { AppMountParameters, CoreStart, CoreSetup, AppUpdater } from '@kbn/core/public'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { PluginServices } from '@kbn/presentation-util-plugin/public'; import { CanvasStartDeps, CanvasSetupDeps } from './plugin'; @@ -75,7 +76,7 @@ export const renderApp = ({ - + @@ -151,7 +152,7 @@ export const initializeCanvas = async ( ], content: (domNode, { hideHelpMenu }) => { ReactDOM.render( - + diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx index d9c665646e2b4..d93a9fb49d359 100644 --- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx +++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx @@ -38,7 +38,7 @@ export const AddEmbeddableFlyout: FC = ({ const embeddablesService = useEmbeddablesService(); const platformService = usePlatformService(); const { getEmbeddableFactories } = embeddablesService; - const { getHttp, getUISettings, getSavedObjectsManagement } = platformService; + const { getContentManagement, getUISettings } = platformService; const onAddPanel = useCallback( (id: string, savedObjectType: string) => { @@ -83,9 +83,8 @@ export const AddEmbeddableFlyout: FC = ({ showFilter={true} noItemsMessage={strings.getNoItemsText()} services={{ + contentClient: getContentManagement().client, uiSettings: getUISettings(), - http: getHttp(), - savedObjectsManagement: getSavedObjectsManagement(), }} /> diff --git a/x-pack/plugins/canvas/public/components/home/home.component.tsx b/x-pack/plugins/canvas/public/components/home/home.component.tsx index dbfbd8a920c7e..c29713da70d11 100644 --- a/x-pack/plugins/canvas/public/components/home/home.component.tsx +++ b/x-pack/plugins/canvas/public/components/home/home.component.tsx @@ -7,7 +7,7 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { KibanaPageTemplate } from '@kbn/kibana-react-plugin/public'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { withSuspense } from '@kbn/presentation-util-plugin/public'; import { WorkpadCreate } from './workpad_create'; @@ -48,7 +48,9 @@ export const Home = ({ activeTab = 'workpads' }: Props) => { ], }} > - {tab === 'workpads' ? : } + + {tab === 'workpads' ? : } + ); }; diff --git a/x-pack/plugins/canvas/public/plugin.tsx b/x-pack/plugins/canvas/public/plugin.tsx index 20dee0bf5fa75..1fec67549eb5a 100644 --- a/x-pack/plugins/canvas/public/plugin.tsx +++ b/x-pack/plugins/canvas/public/plugin.tsx @@ -30,7 +30,7 @@ import { Start as InspectorStart } from '@kbn/inspector-plugin/public'; import { BfetchPublicSetup } from '@kbn/bfetch-plugin/public'; import { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { featureCatalogueEntry } from './feature_catalogue_entry'; import { CanvasAppLocatorDefinition } from '../common/locator'; import { SESSIONSTORAGE_LASTPATH, CANVAS_APP } from '../common/lib/constants'; @@ -68,7 +68,7 @@ export interface CanvasStartDeps { presentationUtil: PresentationUtilPluginStart; visualizations: VisualizationsStart; spaces?: SpacesPluginStart; - savedObjectsManagement: SavedObjectsManagementPluginStart; + contentManagement: ContentManagementPublicStart; } /** diff --git a/x-pack/plugins/canvas/public/services/kibana/platform.ts b/x-pack/plugins/canvas/public/services/kibana/platform.ts index f88851a5e3bd0..1518280ab0688 100644 --- a/x-pack/plugins/canvas/public/services/kibana/platform.ts +++ b/x-pack/plugins/canvas/public/services/kibana/platform.ts @@ -41,6 +41,6 @@ export const platformServiceFactory: CanvaPlatformServiceFactory = ({ getLegacyUrlConflict: startPlugins.spaces?.ui.components.getLegacyUrlConflict, getUISettings: () => coreStart.uiSettings, getHttp: () => coreStart.http, - getSavedObjectsManagement: () => startPlugins.savedObjectsManagement, + getContentManagement: () => startPlugins.contentManagement, }; }; diff --git a/x-pack/plugins/canvas/public/services/platform.ts b/x-pack/plugins/canvas/public/services/platform.ts index 4eeaa8da8f1dc..555f8643fff9b 100644 --- a/x-pack/plugins/canvas/public/services/platform.ts +++ b/x-pack/plugins/canvas/public/services/platform.ts @@ -15,7 +15,7 @@ import { } from '@kbn/core/public'; import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; export interface CanvasPlatformService { getBasePath: () => string; @@ -33,5 +33,5 @@ export interface CanvasPlatformService { getLegacyUrlConflict?: SpacesPluginStart['ui']['components']['getLegacyUrlConflict']; getUISettings: () => IUiSettingsClient; getHttp: () => HttpStart; - getSavedObjectsManagement: () => SavedObjectsManagementPluginStart; + getContentManagement: () => ContentManagementPublicStart; } diff --git a/x-pack/plugins/canvas/public/services/stubs/platform.ts b/x-pack/plugins/canvas/public/services/stubs/platform.ts index 3e40352f9ef59..0726810075251 100644 --- a/x-pack/plugins/canvas/public/services/stubs/platform.ts +++ b/x-pack/plugins/canvas/public/services/stubs/platform.ts @@ -35,5 +35,5 @@ export const platformServiceFactory: CanvasPlatformServiceFactory = () => ({ redirectLegacyUrl: noop, getLegacyUrlConflict: undefined, getHttp: noop, - getSavedObjectsManagement: noop, + getContentManagement: noop, }); diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index c921bde079b5e..45b22e422dc6e 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -79,9 +79,11 @@ "@kbn/babel-register", "@kbn/shared-ux-button-toolbar", "@kbn/saved-objects-finder-plugin", - "@kbn/saved-objects-management-plugin", "@kbn/core-saved-objects-server", "@kbn/discover-utils", + "@kbn/content-management-plugin", + "@kbn/react-kibana-context-theme", + "@kbn/shared-ux-page-kibana-template", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cases/common/constants/index.ts b/x-pack/plugins/cases/common/constants/index.ts index ac689ad5b29d4..01c8543493f7a 100644 --- a/x-pack/plugins/cases/common/constants/index.ts +++ b/x-pack/plugins/cases/common/constants/index.ts @@ -158,6 +158,7 @@ export const READ_CASES_CAPABILITY = 'read_cases' as const; export const UPDATE_CASES_CAPABILITY = 'update_cases' as const; export const DELETE_CASES_CAPABILITY = 'delete_cases' as const; export const PUSH_CASES_CAPABILITY = 'push_cases' as const; +export const CASES_CONNECTORS_CAPABILITY = 'cases_connectors' as const; /** * Cases API Tags @@ -173,6 +174,11 @@ export const SUGGEST_USER_PROFILES_API_TAG = 'casesSuggestUserProfiles'; */ export const BULK_GET_USER_PROFILES_API_TAG = 'bulkGetUserProfiles'; +/** + * This tag is registered for the connectors (configure) get API + */ +export const GET_CONNECTORS_CONFIGURE_API_TAG = 'casesGetConnectorsConfigure'; + /** * User profiles */ diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index d8790a82e5ded..e2e453aaaf278 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -12,7 +12,7 @@ import type { READ_CASES_CAPABILITY, UPDATE_CASES_CAPABILITY, } from '..'; -import type { PUSH_CASES_CAPABILITY } from '../constants'; +import type { CASES_CONNECTORS_CAPABILITY, PUSH_CASES_CAPABILITY } from '../constants'; import type { SnakeToCamelCase } from '../types'; import type { CaseSeverity, @@ -59,6 +59,9 @@ export interface CasesUiConfigType { maxSize?: number; allowedMimeTypes: string[]; }; + stack: { + enabled: boolean; + }; } export const StatusAll = 'all' as const; @@ -285,6 +288,7 @@ export interface CasesPermissions { update: boolean; delete: boolean; push: boolean; + connectors: boolean; } export interface CasesCapabilities { @@ -293,4 +297,5 @@ export interface CasesCapabilities { [UPDATE_CASES_CAPABILITY]: boolean; [DELETE_CASES_CAPABILITY]: boolean; [PUSH_CASES_CAPABILITY]: boolean; + [CASES_CONNECTORS_CAPABILITY]: boolean; } diff --git a/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap b/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap index ea1ef29e71c59..9cca596cc84d8 100644 --- a/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap +++ b/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap @@ -5,6 +5,7 @@ Object { "all": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "casesFilesCasesCreate", "casesFilesCasesRead", ], @@ -14,6 +15,7 @@ Object { "read": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "casesFilesCasesRead", ], } @@ -24,6 +26,7 @@ Object { "all": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "observabilityFilesCasesCreate", "observabilityFilesCasesRead", ], @@ -33,6 +36,7 @@ Object { "read": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "observabilityFilesCasesRead", ], } @@ -43,6 +47,7 @@ Object { "all": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "securitySolutionFilesCasesCreate", "securitySolutionFilesCasesRead", ], @@ -52,6 +57,7 @@ Object { "read": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "securitySolutionFilesCasesRead", ], } diff --git a/x-pack/plugins/cases/common/utils/api_tags.ts b/x-pack/plugins/cases/common/utils/api_tags.ts index d9e3ad25a04c0..2568c0e79b9a0 100644 --- a/x-pack/plugins/cases/common/utils/api_tags.ts +++ b/x-pack/plugins/cases/common/utils/api_tags.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { BULK_GET_USER_PROFILES_API_TAG, SUGGEST_USER_PROFILES_API_TAG } from '../constants'; +import { + BULK_GET_USER_PROFILES_API_TAG, + GET_CONNECTORS_CONFIGURE_API_TAG, + SUGGEST_USER_PROFILES_API_TAG, +} from '../constants'; import { HttpApiTagOperation } from '../constants/types'; import type { Owner } from '../constants/types'; import { constructFilesHttpOperationTag } from '../files'; @@ -16,8 +20,19 @@ export const getApiTags = (owner: Owner) => { const read = constructFilesHttpOperationTag(owner, HttpApiTagOperation.Read); return { - all: [SUGGEST_USER_PROFILES_API_TAG, BULK_GET_USER_PROFILES_API_TAG, create, read] as const, - read: [SUGGEST_USER_PROFILES_API_TAG, BULK_GET_USER_PROFILES_API_TAG, read] as const, + all: [ + SUGGEST_USER_PROFILES_API_TAG, + BULK_GET_USER_PROFILES_API_TAG, + GET_CONNECTORS_CONFIGURE_API_TAG, + create, + read, + ] as const, + read: [ + SUGGEST_USER_PROFILES_API_TAG, + BULK_GET_USER_PROFILES_API_TAG, + GET_CONNECTORS_CONFIGURE_API_TAG, + read, + ] as const, delete: [deleteTag] as const, }; }; diff --git a/x-pack/plugins/cases/common/utils/capabilities.ts b/x-pack/plugins/cases/common/utils/capabilities.ts index a508d11201966..e9c05eda47171 100644 --- a/x-pack/plugins/cases/common/utils/capabilities.ts +++ b/x-pack/plugins/cases/common/utils/capabilities.ts @@ -6,6 +6,7 @@ */ import { + CASES_CONNECTORS_CAPABILITY, CREATE_CASES_CAPABILITY, DELETE_CASES_CAPABILITY, PUSH_CASES_CAPABILITY, @@ -23,7 +24,8 @@ export const createUICapabilities = () => ({ READ_CASES_CAPABILITY, UPDATE_CASES_CAPABILITY, PUSH_CASES_CAPABILITY, + CASES_CONNECTORS_CAPABILITY, ] as const, - read: [READ_CASES_CAPABILITY] as const, + read: [READ_CASES_CAPABILITY, CASES_CONNECTORS_CAPABILITY] as const, delete: [DELETE_CASES_CAPABILITY] as const, }); diff --git a/x-pack/plugins/cases/kibana.jsonc b/x-pack/plugins/cases/kibana.jsonc index b0a03ad753e97..6b7cc4450985a 100644 --- a/x-pack/plugins/cases/kibana.jsonc +++ b/x-pack/plugins/cases/kibana.jsonc @@ -28,7 +28,7 @@ "ruleRegistry", "files", "savedObjectsFinder", - "savedObjectsManagement", + "contentManagement", "uiActions", ], "optionalPlugins": [ diff --git a/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts b/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts index 9898f9c374c78..5b82919523f36 100644 --- a/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts +++ b/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts @@ -11,8 +11,8 @@ import { allCasesPermissions, noCasesCapabilities, noCasesPermissions, - readCasesCapabilities, readCasesPermissions, + readCasesCapabilities, writeCasesCapabilities, writeCasesPermissions, } from '../../common/mock'; @@ -77,6 +77,12 @@ const hasSecurityWriteAndObservabilityRead: CasesCapabilities = { generalCases: noCasesCapabilities(), }; +const hasSecurityConnectors: CasesCapabilities = { + securitySolutionCases: readCasesCapabilities(), + observabilityCases: noCasesCapabilities(), + generalCases: noCasesCapabilities(), +}; + describe('canUseCases', () => { it.each([hasAll, hasSecurity, hasObservability, hasSecurityWriteAndObservabilityRead])( 'returns true for all permissions, if a user has access to both on any solution', @@ -109,4 +115,12 @@ describe('canUseCases', () => { expect(permissions).toStrictEqual(noCasesPermissions()); } ); + + it.each([hasSecurityConnectors])( + 'returns true for only connectors, if a user has access to only connectors on any solution', + (capability) => { + const permissions = canUseCases(capability)(); + expect(permissions).toStrictEqual(readCasesPermissions()); + } + ); }); diff --git a/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts b/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts index 34af1c3865da5..1cc22c0799702 100644 --- a/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts +++ b/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts @@ -40,8 +40,10 @@ export const canUseCases = acc.update = acc.update || userCapabilitiesForOwner.update; acc.delete = acc.delete || userCapabilitiesForOwner.delete; acc.push = acc.push || userCapabilitiesForOwner.push; - const allFromAcc = acc.create && acc.read && acc.update && acc.delete && acc.push; + const allFromAcc = + acc.create && acc.read && acc.update && acc.delete && acc.push && acc.connectors; acc.all = acc.all || userCapabilitiesForOwner.all || allFromAcc; + acc.connectors = acc.connectors || userCapabilitiesForOwner.connectors; return acc; }, @@ -52,6 +54,7 @@ export const canUseCases = update: false, delete: false, push: false, + connectors: false, } ); diff --git a/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts b/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts index 58d6d61e80324..a3f741f373032 100644 --- a/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts +++ b/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts @@ -12,6 +12,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities(undefined)).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -25,6 +26,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities()).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -38,6 +40,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities({ create_cases: true })).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": true, "delete": false, "push": false, @@ -55,10 +58,12 @@ describe('getUICapabilities', () => { update_cases: false, delete_cases: false, push_cases: false, + cases_connectors: false, }) ).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -72,6 +77,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities({})).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -89,10 +95,35 @@ describe('getUICapabilities', () => { update_cases: true, delete_cases: true, push_cases: true, + cases_connectors: true, }) ).toMatchInlineSnapshot(` Object { "all": false, + "connectors": true, + "create": false, + "delete": true, + "push": true, + "read": true, + "update": true, + } + `); + }); + + it('returns false for the all field when cases_connectors is false', () => { + expect( + getUICapabilities({ + create_cases: false, + read_cases: true, + update_cases: true, + delete_cases: true, + push_cases: true, + cases_connectors: false, + }) + ).toMatchInlineSnapshot(` + Object { + "all": false, + "connectors": false, "create": false, "delete": true, "push": true, diff --git a/x-pack/plugins/cases/public/client/helpers/capabilities.ts b/x-pack/plugins/cases/public/client/helpers/capabilities.ts index f09ac84448952..278512fef623c 100644 --- a/x-pack/plugins/cases/public/client/helpers/capabilities.ts +++ b/x-pack/plugins/cases/public/client/helpers/capabilities.ts @@ -7,6 +7,7 @@ import type { CasesPermissions } from '../../../common'; import { + CASES_CONNECTORS_CAPABILITY, CREATE_CASES_CAPABILITY, DELETE_CASES_CAPABILITY, PUSH_CASES_CAPABILITY, @@ -22,7 +23,8 @@ export const getUICapabilities = ( const update = !!featureCapabilities?.[UPDATE_CASES_CAPABILITY]; const deletePriv = !!featureCapabilities?.[DELETE_CASES_CAPABILITY]; const push = !!featureCapabilities?.[PUSH_CASES_CAPABILITY]; - const all = create && read && update && deletePriv && push; + const connectors = !!featureCapabilities?.[CASES_CONNECTORS_CAPABILITY]; + const all = create && read && update && deletePriv && push && connectors; return { all, @@ -31,5 +33,6 @@ export const getUICapabilities = ( update, delete: deletePriv, push, + connectors, }; }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts index 812840b1553e3..c540824b1ebb5 100644 --- a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts @@ -193,6 +193,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => { update: permissions.update, delete: permissions.delete, push: permissions.push, + connectors: permissions.connectors, }, visualize: { crud: !!capabilities.visualize?.save, read: !!capabilities.visualize?.show }, dashboard: { @@ -213,6 +214,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => { permissions.update, permissions.delete, permissions.push, + permissions.connectors, ] ); }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx index 819c7099b5cb8..31ea452874c28 100644 --- a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx +++ b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx @@ -74,6 +74,7 @@ export const createStartServicesMock = ({ license }: StartServiceArgs = {}): Sta update_cases: true, delete_cases: true, push_cases: true, + cases_connectors: true, }, visualize: { save: true, show: true }, dashboard: { show: true, createNew: true }, diff --git a/x-pack/plugins/cases/public/common/mock/permissions.ts b/x-pack/plugins/cases/public/common/mock/permissions.ts index 01d1dc64952ed..4d68e9d36c776 100644 --- a/x-pack/plugins/cases/public/common/mock/permissions.ts +++ b/x-pack/plugins/cases/public/common/mock/permissions.ts @@ -9,9 +9,23 @@ import type { CasesCapabilities, CasesPermissions } from '../../containers/types export const allCasesPermissions = () => buildCasesPermissions(); export const noCasesPermissions = () => - buildCasesPermissions({ read: false, create: false, update: false, delete: false, push: false }); + buildCasesPermissions({ + read: false, + create: false, + update: false, + delete: false, + push: false, + connectors: false, + }); export const readCasesPermissions = () => - buildCasesPermissions({ read: true, create: false, update: false, delete: false, push: false }); + buildCasesPermissions({ + read: true, + create: false, + update: false, + delete: false, + push: false, + connectors: true, + }); export const noCreateCasesPermissions = () => buildCasesPermissions({ create: false }); export const noUpdateCasesPermissions = () => buildCasesPermissions({ update: false }); export const noPushCasesPermissions = () => buildCasesPermissions({ push: false }); @@ -19,6 +33,7 @@ export const noDeleteCasesPermissions = () => buildCasesPermissions({ delete: fa export const writeCasesPermissions = () => buildCasesPermissions({ read: false }); export const onlyDeleteCasesPermission = () => buildCasesPermissions({ read: false, create: false, update: false, delete: true, push: false }); +export const noConnectorsCasePermission = () => buildCasesPermissions({ connectors: false }); export const buildCasesPermissions = (overrides: Partial> = {}) => { const create = overrides.create ?? true; @@ -26,6 +41,7 @@ export const buildCasesPermissions = (overrides: Partial update_cases: false, delete_cases: false, push_cases: false, + cases_connectors: false, }); export const readCasesCapabilities = () => buildCasesCapabilities({ @@ -67,5 +85,6 @@ export const buildCasesCapabilities = (overrides?: Partial) = update_cases: overrides?.update_cases ?? true, delete_cases: overrides?.delete_cases ?? true, push_cases: overrides?.push_cases ?? true, + cases_connectors: overrides?.cases_connectors ?? true, }; }; diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx index c77340b4f37ac..9db8d82c6b315 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx @@ -12,8 +12,12 @@ import { render, screen } from '@testing-library/react'; import type { Props } from './connectors'; import { Connectors } from './connectors'; -import type { AppMockRenderer } from '../../common/mock'; -import { createAppMockRenderer, TestProviders } from '../../common/mock'; +import { + type AppMockRenderer, + noConnectorsCasePermission, + createAppMockRenderer, + TestProviders, +} from '../../common/mock'; import { ConnectorsDropdown } from './connectors_dropdown'; import { connectors, actionTypes } from './__mock__'; import { ConnectorTypes } from '../../../common/types/domain'; @@ -161,4 +165,14 @@ describe('Connectors', () => { ).toBeInTheDocument(); expect(result.queryByTestId('case-connectors-dropdown')).toBe(null); }); + + it('shows the actions permission message if the user does not have access to case connector', async () => { + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + expect( + result.getByTestId('configure-case-connector-permissions-error-msg') + ).toBeInTheDocument(); + expect(result.queryByTestId('case-connectors-dropdown')).toBe(null); + }); }); diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx index 0b51323f3ffd8..87e3100f087a7 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx @@ -27,6 +27,7 @@ import { ConnectorTypes } from '../../../common/types/domain'; import { DeprecatedCallout } from '../connectors/deprecated_callout'; import { isDeprecatedConnector } from '../utils'; import { useApplicationCapabilities } from '../../common/lib/kibana'; +import { useCasesContext } from '../cases_context/use_cases_context'; const EuiFormRowExtended = styled(EuiFormRow)` .euiFormRow__labelWrapper { @@ -63,6 +64,8 @@ const ConnectorsComponent: React.FC = ({ () => connectors.find((c) => c.id === selectedConnector.id), [connectors, selectedConnector.id] ); + const { permissions } = useCasesContext(); + const canUseConnectors = permissions.connectors && actions.read; const connectorsName = connector?.name ?? 'none'; @@ -105,7 +108,7 @@ const ConnectorsComponent: React.FC = ({ > - {actions.read ? ( + {canUseConnectors ? ( { expect(result.getByTestId('create-case-connector-permissions-error-msg')).toBeInTheDocument(); expect(result.queryByTestId('caseConnectors')).toBe(null); }); + + it('shows the actions permission message if the user does not have access to case connector', async () => { + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render( + + + + ); + expect(result.getByTestId('create-case-connector-permissions-error-msg')).toBeInTheDocument(); + expect(result.queryByTestId('caseConnectors')).toBe(null); + }); }); diff --git a/x-pack/plugins/cases/public/components/create/connector.tsx b/x-pack/plugins/cases/public/components/create/connector.tsx index 58bf659b68cf5..7422e671fa4bb 100644 --- a/x-pack/plugins/cases/public/components/create/connector.tsx +++ b/x-pack/plugins/cases/public/components/create/connector.tsx @@ -18,6 +18,7 @@ import { useCaseConfigure } from '../../containers/configure/use_configure'; import { getConnectorById, getConnectorsFormValidators } from '../utils'; import { useApplicationCapabilities } from '../../common/lib/kibana'; import * as i18n from '../../common/translations'; +import { useCasesContext } from '../cases_context/use_cases_context'; interface Props { connectors: ActionConnector[]; @@ -30,6 +31,8 @@ const ConnectorComponent: React.FC = ({ connectors, isLoading, isLoadingC const connector = getConnectorById(connectorId, connectors) ?? null; const { connector: configurationConnector } = useCaseConfigure(); const { actions } = useApplicationCapabilities(); + const { permissions } = useCasesContext(); + const hasReadPermissions = permissions.connectors && actions.read; const defaultConnectorId = useMemo(() => { return connectors.some((c) => c.id === configurationConnector.id) @@ -42,7 +45,7 @@ const ConnectorComponent: React.FC = ({ connectors, isLoading, isLoadingC connectors, }); - if (!actions.read) { + if (!hasReadPermissions) { return ( {i18n.READ_ACTIONS_PERMISSIONS_ERROR_MSG} diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx index 21ff3af65a336..b68641526c46a 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx @@ -11,12 +11,14 @@ import userEvent from '@testing-library/user-event'; import type { EditConnectorProps } from '.'; import { EditConnector } from '.'; -import type { AppMockRenderer } from '../../common/mock'; + import { + type AppMockRenderer, createAppMockRenderer, readCasesPermissions, noPushCasesPermissions, TestProviders, + noConnectorsCasePermission, } from '../../common/mock'; import { basicCase, connectorsMock } from '../../containers/mock'; import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; @@ -274,6 +276,17 @@ describe('EditConnector ', () => { }); }); + it('does not show the callout if the user does not have access to cases connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + await waitFor(() => { + expect(result.getByTestId('edit-connector-permissions-error-msg')).toBeInTheDocument(); + expect(result.queryByTestId('push-callouts')).toBe(null); + }); + }); + it('does not show the connectors previewer if the user does not have read access to actions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { @@ -285,6 +298,14 @@ describe('EditConnector ', () => { expect(result.queryByTestId('connector-fields-preview')).not.toBeInTheDocument(); }); + it('does not show the connectors previewer if the user does not have access to cases connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + expect(result.queryByTestId('connector-fields-preview')).not.toBeInTheDocument(); + }); + it('does not show the connectors form if the user does not have read access to actions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { @@ -296,6 +317,14 @@ describe('EditConnector ', () => { expect(result.queryByTestId('edit-connector-fields-form-flex-item')).not.toBeInTheDocument(); }); + it('does not show the connectors form if the user does not have access to cases connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + expect(result.queryByTestId('edit-connector-fields-form-flex-item')).not.toBeInTheDocument(); + }); + it('does not show the push button if the user does not have read access to actions', async () => { appMockRender.coreStart.application.capabilities = { ...appMockRender.coreStart.application.capabilities, @@ -317,6 +346,15 @@ describe('EditConnector ', () => { }); }); + it('does not show the push button if the user does not have access to cases actions', async () => { + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + await waitFor(() => { + expect(result.queryByTestId('push-to-external-service')).toBe(null); + }); + }); + it('does not show the edit connectors pencil if the user does not have read access to actions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { @@ -332,6 +370,20 @@ describe('EditConnector ', () => { }); }); + it('does not show the edit connectors pencil if the user does not have access to case connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ + permissions: noConnectorsCasePermission(), + }); + + appMockRender.render(); + + await waitFor(() => { + expect(screen.getByTestId('connector-edit-header')).toBeInTheDocument(); + expect(screen.queryByTestId('connector-edit-button')).not.toBeInTheDocument(); + }); + }); + it('does not show the edit connectors pencil if the user does not have push permissions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender = createAppMockRenderer({ permissions: noPushCasesPermissions() }); diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.tsx index ee819af75b400..27d5e75b550fb 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.tsx @@ -21,6 +21,7 @@ import { PushButton } from './push_button'; import { PushCallouts } from './push_callouts'; import { ConnectorsForm } from './connectors_form'; import { ConnectorFieldsPreviewForm } from '../connectors/fields_preview_form'; +import { useCasesContext } from '../cases_context/use_cases_context'; export interface EditConnectorProps { caseData: CaseUI; @@ -45,7 +46,8 @@ export const EditConnector = React.memo( const [isEdit, setIsEdit] = useState(false); const { actions } = useApplicationCapabilities(); - const hasActionsReadPermissions = actions.read; + const { permissions } = useCasesContext(); + const canUseConnectors = permissions.connectors && actions.read; const onEditClick = useCallback(() => setIsEdit(true), []); const onCancelConnector = useCallback(() => setIsEdit(false), []); @@ -102,7 +104,7 @@ export const EditConnector = React.memo(

{i18n.CONNECTORS}

- {!isLoading && !isEdit && hasPushPermissions && hasActionsReadPermissions ? ( + {!isLoading && !isEdit && hasPushPermissions && canUseConnectors ? ( - {!isLoading && !isEdit && hasErrorMessages && hasActionsReadPermissions && ( + {!isLoading && !isEdit && hasErrorMessages && canUseConnectors && ( )} - {!hasActionsReadPermissions && ( + {!canUseConnectors && ( {i18n.READ_ACTIONS_PERMISSIONS_ERROR_MSG} )} - {hasActionsReadPermissions && !isEdit && ( + {canUseConnectors && !isEdit && ( )} - {hasActionsReadPermissions && isEdit && ( + {canUseConnectors && isEdit && ( )} - {!hasErrorMessages && - !isLoading && - !isEdit && - hasPushPermissions && - hasActionsReadPermissions && ( - - - 0 || !needsToBePushed || !hasPushPermissions} - connectorName={connectorWithName.name} - /> - - - )} + {!hasErrorMessages && !isLoading && !isEdit && hasPushPermissions && canUseConnectors && ( + + + 0 || !needsToBePushed || !hasPushPermissions} + connectorName={connectorWithName.name} + /> + + + )}
diff --git a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx index 996d3d757186d..14d669c09c2fb 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx @@ -71,9 +71,8 @@ const LensEditorComponent: LensEuiMarkdownEditorUiPlugin['editor'] = ({ embeddable, lens, storage, - http, + contentManagement, uiSettings, - savedObjectsManagement, data: { query: { timefilter: { timefilter }, @@ -331,9 +330,8 @@ const LensEditorComponent: LensEuiMarkdownEditorUiPlugin['editor'] = ({ savedObjectMetaData={savedObjectMetaData} fixedPageSize={10} services={{ + contentClient: contentManagement.client, uiSettings, - http, - savedObjectsManagement, }} leftChildren={createLensButton} helpText={i18n.translate( diff --git a/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx b/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx index 77d38e87a2c41..bab441edbe80c 100644 --- a/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx @@ -587,28 +587,60 @@ describe('createCommentUserActionBuilder', () => { }); }); - it('renders correctly an action', async () => { - const userAction = getHostIsolationUserAction(); - - const builder = createCommentUserActionBuilder({ - ...builderArgs, - caseData: { - ...builderArgs.caseData, - comments: [hostIsolationComment()], - }, - userAction, + describe('Host isolation action', () => { + it('renders correctly an action', async () => { + const userAction = getHostIsolationUserAction(); + + const builder = createCommentUserActionBuilder({ + ...builderArgs, + caseData: { + ...builderArgs.caseData, + comments: [hostIsolationComment()], + }, + userAction, + }); + + const createdUserAction = builder.build(); + render( + + + + ); + + expect(screen.getByTestId('endpoint-action')).toBeInTheDocument(); + expect(screen.getByText('submitted isolate request on host')).toBeInTheDocument(); + expect(screen.getByText('host1')).toBeInTheDocument(); + expect(screen.getByText('I just isolated the host!')).toBeInTheDocument(); }); - const createdUserAction = builder.build(); - render( - - - - ); + it('shows the correct username', async () => { + const createdBy = { profileUid: userProfiles[0].uid }; + const userAction = getHostIsolationUserAction({ + createdBy, + }); - expect(screen.getByText('submitted isolate request on host')).toBeInTheDocument(); - expect(screen.getByText('host1')).toBeInTheDocument(); - expect(screen.getByText('I just isolated the host!')).toBeInTheDocument(); + const builder = createCommentUserActionBuilder({ + ...builderArgs, + caseData: { + ...builderArgs.caseData, + comments: [hostIsolationComment({ createdBy })], + }, + userAction, + }); + + const createdUserAction = builder.build(); + render( + + + + ); + + expect( + screen.getAllByTestId('case-user-profile-avatar-damaged_raccoon')[0] + ).toBeInTheDocument(); + expect(screen.getAllByText('DR')[0]).toBeInTheDocument(); + expect(screen.getAllByText('Damaged Raccoon')[0]).toBeInTheDocument(); + }); }); describe('Attachment framework', () => { diff --git a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx index 07a35e723878d..4530e115a2f04 100644 --- a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx @@ -13,18 +13,11 @@ import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; import routeData from 'react-router'; import { useUpdateComment } from '../../containers/use_update_comment'; -import { - basicCase, - caseUserActions, - getHostIsolationUserAction, - getUserAction, - hostIsolationComment, -} from '../../containers/mock'; +import { basicCase, caseUserActions, getUserAction } from '../../containers/mock'; import { UserActions } from '.'; import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; import { UserActionActions } from '../../../common/types/domain'; -import { userProfiles, userProfilesMap } from '../../containers/user_profiles/api.mock'; import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; import type { UserActivityParams } from '../user_actions_activity_bar/types'; import { useFindCaseUserActions } from '../../containers/use_find_case_user_actions'; @@ -325,299 +318,4 @@ describe.skip(`UserActions`, () => { expect(screen.getAllByTestId('add-comment')[1].textContent).toContain(newComment); }); }); - - // FLAKY: https://github.com/elastic/kibana/issues/156742 - describe.skip('Host isolation action', () => { - it('renders in the cases details view', async () => { - const isolateAction = [getHostIsolationUserAction()]; - const props = { - ...defaultProps, - data: { ...defaultProps.data, comments: [...basicCase.comments, hostIsolationComment()] }, - }; - - useFindCaseUserActionsMock.mockReturnValue({ - ...defaultUseFindCaseUserActions, - data: { userActions: isolateAction }, - }); - - appMockRender.render(); - await waitFor(() => { - expect(screen.getByTestId('endpoint-action')).toBeInTheDocument(); - }); - }); - - it('shows the correct username', async () => { - const isolateAction = [ - getHostIsolationUserAction({ createdBy: { profileUid: userProfiles[0].uid } }), - ]; - const props = { - ...defaultProps, - userProfiles: userProfilesMap, - data: { - ...defaultProps.data, - comments: [hostIsolationComment({ createdBy: { profileUid: userProfiles[0].uid } })], - }, - }; - - useFindCaseUserActionsMock.mockReturnValue({ - ...defaultUseFindCaseUserActions, - data: { userActions: isolateAction }, - }); - - appMockRender.render(); - - expect( - screen.getAllByTestId('case-user-profile-avatar-damaged_raccoon')[0] - ).toBeInTheDocument(); - expect(screen.getAllByText('DR')[0]).toBeInTheDocument(); - expect(screen.getAllByText('Damaged Raccoon')[0]).toBeInTheDocument(); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/156750 - // FLAKY: https://github.com/elastic/kibana/issues/156749 - // FLAKY: https://github.com/elastic/kibana/issues/156748 - // FLAKY: https://github.com/elastic/kibana/issues/156747 - // FLAKY: https://github.com/elastic/kibana/issues/156746 - // FLAKY: https://github.com/elastic/kibana/issues/156745 - // FLAKY: https://github.com/elastic/kibana/issues/156744 - // FLAKY: https://github.com/elastic/kibana/issues/156743 - describe.skip('pagination', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('Loading spinner when user actions loading', () => { - useFindCaseUserActionsMock.mockReturnValue({ isLoading: true }); - useInfiniteFindCaseUserActionsMock.mockReturnValue({ isLoading: true }); - appMockRender.render( - - ); - - expect(screen.getByTestId('user-actions-loading')).toBeInTheDocument(); - }); - - it('renders two user actions list when user actions are more than 10', () => { - appMockRender.render(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(2); - }); - - it('renders only one user actions list when last page is 0', async () => { - useFindCaseUserActionsMock.mockReturnValue({ - ...defaultUseFindCaseUserActions, - data: { userActions: [] }, - }); - const props = { - ...defaultProps, - userActionsStats: { - total: 0, - totalComments: 0, - totalOtherActions: 0, - }, - }; - - appMockRender.render(); - - await waitForComponentToUpdate(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(1); - }); - - it('renders only one user actions list when last page is 1', async () => { - useFindCaseUserActionsMock.mockReturnValue({ - ...defaultUseFindCaseUserActions, - data: { userActions: [] }, - }); - const props = { - ...defaultProps, - userActionsStats: { - total: 1, - totalComments: 0, - totalOtherActions: 1, - }, - }; - - appMockRender.render(); - - await waitForComponentToUpdate(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(1); - }); - - it('renders only one action list when user actions are less than or equal to 10', async () => { - useFindCaseUserActionsMock.mockReturnValue({ - ...defaultUseFindCaseUserActions, - data: { userActions: [] }, - }); - const props = { - ...defaultProps, - userActionsStats: { - total: 10, - totalComments: 6, - totalOtherActions: 4, - }, - }; - - appMockRender.render(); - - await waitForComponentToUpdate(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(1); - }); - - it('shows more button visible when hasNext page is true', async () => { - useInfiniteFindCaseUserActionsMock.mockReturnValue({ - ...defaultInfiniteUseFindCaseUserActions, - hasNextPage: true, - }); - const props = { - ...defaultProps, - userActionsStats: { - total: 25, - totalComments: 10, - totalOtherActions: 15, - }, - }; - - appMockRender.render(); - - await waitForComponentToUpdate(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(2); - expect(screen.getByTestId('cases-show-more-user-actions')).toBeInTheDocument(); - }); - - it('call fetchNextPage on showMore button click', async () => { - useInfiniteFindCaseUserActionsMock.mockReturnValue({ - ...defaultInfiniteUseFindCaseUserActions, - hasNextPage: true, - }); - const props = { - ...defaultProps, - userActionsStats: { - total: 25, - totalComments: 10, - totalOtherActions: 15, - }, - }; - - appMockRender.render(); - - await waitForComponentToUpdate(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(2); - - const showMore = screen.getByTestId('cases-show-more-user-actions'); - - expect(showMore).toBeInTheDocument(); - - userEvent.click(showMore); - - await waitFor(() => { - expect(defaultInfiniteUseFindCaseUserActions.fetchNextPage).toHaveBeenCalled(); - }); - }); - - it('shows more button visible 21st user action added', async () => { - const mockUserActions = [ - ...caseUserActions, - getUserAction('comment', UserActionActions.create), - getUserAction('comment', UserActionActions.update), - getUserAction('comment', UserActionActions.create), - getUserAction('comment', UserActionActions.update), - getUserAction('comment', UserActionActions.create), - getUserAction('comment', UserActionActions.update), - getUserAction('comment', UserActionActions.create), - ]; - useInfiniteFindCaseUserActionsMock.mockReturnValue({ - ...defaultInfiniteUseFindCaseUserActions, - data: { - pages: [ - { - total: 20, - page: 1, - perPage: 10, - userActions: mockUserActions, - }, - ], - }, - }); - useFindCaseUserActionsMock.mockReturnValue({ - ...defaultUseFindCaseUserActions, - data: { - total: 20, - page: 2, - perPage: 10, - userActions: mockUserActions, - }, - }); - const props = { - ...defaultProps, - userActionsStats: { - total: 20, - totalComments: 10, - totalOtherActions: 10, - }, - }; - - const { rerender } = appMockRender.render(); - - await waitForComponentToUpdate(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(2); - expect(screen.queryByTestId('cases-show-more-user-actions')).not.toBeInTheDocument(); - - useInfiniteFindCaseUserActionsMock.mockReturnValue({ - ...defaultInfiniteUseFindCaseUserActions, - data: { - pages: [ - { - total: 21, - page: 1, - perPage: 10, - userActions: mockUserActions, - }, - { - total: 21, - page: 2, - perPage: 10, - userActions: [getUserAction('comment', UserActionActions.create)], - }, - ], - }, - hasNextPage: true, - }); - useFindCaseUserActionsMock.mockReturnValue({ - ...defaultUseFindCaseUserActions, - data: { - total: 21, - page: 2, - perPage: 10, - userActions: mockUserActions, - }, - }); - - const newProps = { - ...props, - userActionsStats: { - total: 21, - totalComments: 11, - totalOtherActions: 10, - }, - }; - - rerender(); - - await waitForComponentToUpdate(); - - expect(screen.getAllByTestId('user-actions-list')).toHaveLength(2); - - const firstUserActionsList = screen.getAllByTestId('user-actions-list')[0]; - - expect(firstUserActionsList.getElementsByTagName('li')).toHaveLength(11); - - expect(screen.getByTestId('cases-show-more-user-actions')).toBeInTheDocument(); - }); - }); }); diff --git a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx index 0bff8bce61c3e..e98d63debce4b 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx @@ -11,14 +11,17 @@ import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; import * as i18n from './translations'; import { casesQueriesKeys } from '../constants'; import type { ServerError } from '../../types'; +import { useCasesContext } from '../../components/cases_context/use_cases_context'; export function useGetSupportedActionConnectors() { const toasts = useToasts(); const { actions } = useApplicationCapabilities(); + const { permissions } = useCasesContext(); + return useQuery( casesQueriesKeys.connectorsList(), async ({ signal }) => { - if (!actions.read) { + if (!actions.read || !permissions.connectors) { return []; } return getSupportedActionConnectors({ signal }); diff --git a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx index 36cbd9417e375..c6a05daff7f4e 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { renderHook } from '@testing-library/react-hooks'; import * as api from './api'; -import { TestProviders } from '../../common/mock'; +import { noConnectorsCasePermission, TestProviders } from '../../common/mock'; import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; import { useGetSupportedActionConnectors } from './use_get_supported_action_connectors'; @@ -65,4 +65,20 @@ describe('useConnectors', () => { expect(spyOnFetchConnectors).not.toHaveBeenCalled(); expect(result.current.data).toEqual([]); }); + + it('does not fetch connectors when the user does not has access to connectors', async () => { + const spyOnFetchConnectors = jest.spyOn(api, 'getSupportedActionConnectors'); + useApplicationCapabilitiesMock().actions = { crud: true, read: true }; + + const { result, waitForNextUpdate } = renderHook(() => useGetSupportedActionConnectors(), { + wrapper: ({ children }) => ( + {children} + ), + }); + + await waitForNextUpdate(); + + expect(spyOnFetchConnectors).not.toHaveBeenCalled(); + expect(result.current.data).toEqual([]); + }); }); diff --git a/x-pack/plugins/cases/public/plugin.test.ts b/x-pack/plugins/cases/public/plugin.test.ts new file mode 100644 index 0000000000000..bb81b7d501c5d --- /dev/null +++ b/x-pack/plugins/cases/public/plugin.test.ts @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { PluginInitializerContext } from '@kbn/core/public'; +import { coreMock } from '@kbn/core/public/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +import { featuresPluginMock } from '@kbn/features-plugin/public/mocks'; +import { securityMock } from '@kbn/security-plugin/public/mocks'; +import { managementPluginMock } from '@kbn/management-plugin/public/mocks'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; +import { lensPluginMock } from '@kbn/lens-plugin/public/mocks'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; +import { mockStorage } from '@kbn/kibana-utils-plugin/public/storage/hashed_item_store/mock'; +import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; +import type { CasesPluginSetup, CasesPluginStart } from './types'; +import { CasesUiPlugin } from './plugin'; +import { ALLOWED_MIME_TYPES } from '../common/constants/mime_types'; + +function getConfig(overrides = {}) { + return { + markdownPlugins: { lens: true }, + files: { maxSize: 1, allowedMimeTypes: ALLOWED_MIME_TYPES }, + stack: { enabled: true }, + ...overrides, + }; +} + +describe('Cases Ui Plugin', () => { + let context: PluginInitializerContext; + let plugin: CasesUiPlugin; + let coreSetup: ReturnType; + let coreStart: ReturnType; + let pluginsSetup: jest.Mocked; + let pluginsStart: jest.Mocked; + + beforeEach(() => { + context = coreMock.createPluginInitializerContext(getConfig()); + plugin = new CasesUiPlugin(context); + coreSetup = coreMock.createSetup(); + coreStart = coreMock.createStart(); + + pluginsSetup = { + files: { + filesClientFactory: { asScoped: jest.fn(), asUnscoped: jest.fn() }, + registerFileKind: jest.fn(), + }, + security: securityMock.createSetup(), + management: managementPluginMock.createSetupContract(), + }; + + pluginsStart = { + licensing: licensingMock.createStart(), + uiActions: uiActionsPluginMock.createStartContract(), + files: { + filesClientFactory: { asScoped: jest.fn(), asUnscoped: jest.fn() }, + getAllFindKindDefinitions: jest.fn(), + getFileKindDefinition: jest.fn(), + }, + features: featuresPluginMock.createStart(), + security: securityMock.createStart(), + data: dataPluginMock.createStartContract(), + embeddable: embeddablePluginMock.createStartContract(), + lens: lensPluginMock.createStartContract(), + contentManagement: contentManagementMock.createStartContract(), + storage: { + store: { + getItem: mockStorage.getItem, + setItem: mockStorage.setItem, + removeItem: mockStorage.removeItem, + clear: mockStorage.clear, + }, + get: jest.fn(), + set: jest.fn(), + clear: jest.fn(), + remove: jest.fn(), + }, + triggersActionsUi: triggersActionsUiMock.createStart(), + }; + }); + + describe('setup()', () => { + it('should start setup cases plugin correctly', async () => { + const setup = plugin.setup(coreSetup, pluginsSetup); + + expect(setup).toMatchInlineSnapshot(` + Object { + "attachmentFramework": Object { + "registerExternalReference": [Function], + "registerPersistableState": [Function], + }, + } + `); + }); + + it('should register kibana feature when stack is enabled', async () => { + plugin.setup(coreSetup, pluginsSetup); + + expect( + pluginsSetup.management.sections.section.insightsAndAlerting.registerApp + ).toHaveBeenCalled(); + }); + + it('should not register kibana feature when stack is disabled', async () => { + context = coreMock.createPluginInitializerContext(getConfig({ stack: { enabled: false } })); + const pluginWithStackDisabled = new CasesUiPlugin(context); + + pluginWithStackDisabled.setup(coreSetup, pluginsSetup); + + expect( + pluginsSetup.management.sections.section.insightsAndAlerting.registerApp + ).not.toHaveBeenCalled(); + }); + }); + + describe('start', () => { + it('should start cases plugin correctly', async () => { + const pluginStart = plugin.start(coreStart, pluginsStart); + + expect(pluginStart).toStrictEqual({ + api: { + cases: { + bulkGet: expect.any(Function), + find: expect.any(Function), + getCasesMetrics: expect.any(Function), + getCasesStatus: expect.any(Function), + }, + getRelatedCases: expect.any(Function), + }, + helpers: { + canUseCases: expect.any(Function), + getRuleIdFromEvent: expect.any(Function), + getUICapabilities: expect.any(Function), + groupAlertsByRule: expect.any(Function), + }, + hooks: { + useCasesAddToExistingCaseModal: expect.any(Function), + useCasesAddToNewCaseFlyout: expect.any(Function), + }, + ui: { + getAllCasesSelectorModal: expect.any(Function), + getCases: expect.any(Function), + getCasesContext: expect.any(Function), + getRecentCases: expect.any(Function), + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts index 82d62f2c07da7..f4909f344e7fc 100644 --- a/x-pack/plugins/cases/public/plugin.ts +++ b/x-pack/plugins/cases/public/plugin.ts @@ -75,30 +75,32 @@ export class CasesUiPlugin }); } - plugins.management.sections.section.insightsAndAlerting.registerApp({ - id: APP_ID, - title: APP_TITLE, - order: 1, - async mount(params: ManagementAppMountParams) { - const [coreStart, pluginsStart] = (await core.getStartServices()) as [ - CoreStart, - CasesPluginStart, - unknown - ]; - - const { renderApp } = await import('./application'); - - return renderApp({ - mountParams: params, - coreStart, - pluginsStart, - storage, - kibanaVersion, - externalReferenceAttachmentTypeRegistry, - persistableStateAttachmentTypeRegistry, - }); - }, - }); + if (config.stack.enabled) { + plugins.management.sections.section.insightsAndAlerting.registerApp({ + id: APP_ID, + title: APP_TITLE, + order: 1, + async mount(params: ManagementAppMountParams) { + const [coreStart, pluginsStart] = (await core.getStartServices()) as [ + CoreStart, + CasesPluginStart, + unknown + ]; + + const { renderApp } = await import('./application'); + + return renderApp({ + mountParams: params, + coreStart, + pluginsStart, + storage, + kibanaVersion, + externalReferenceAttachmentTypeRegistry, + persistableStateAttachmentTypeRegistry, + }); + }, + }); + } return { attachmentFramework: { diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index c9c74a48989a8..ee00fc034c1d6 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -23,7 +23,7 @@ import type { DistributiveOmit } from '@elastic/eui'; import type { ApmBase } from '@elastic/apm-rum'; import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { FilesSetup, FilesStart } from '@kbn/files-plugin/public'; -import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; @@ -74,7 +74,7 @@ export interface CasesPluginStart { files: FilesStart; lens: LensPublicStart; licensing?: LicensingPluginStart; - savedObjectsManagement: SavedObjectsManagementPluginStart; + contentManagement: ContentManagementPublicStart; security: SecurityPluginStart; serverless?: ServerlessPluginStart; spaces?: SpacesPluginStart; diff --git a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap index 18eef843e88c2..ebb9501ff8960 100644 --- a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap +++ b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap @@ -2458,7 +2458,7 @@ Object { "type": "cases", }, }, - "message": "Failed attempt to update cases [id=1] as owner \\"awesome\\"", + "message": "Failed attempt to push cases [id=1] as owner \\"awesome\\"", } `; @@ -2478,7 +2478,7 @@ Object { "change", ], }, - "message": "Failed attempt to update a case as any owners", + "message": "Failed attempt to push a case as any owners", } `; @@ -2500,7 +2500,7 @@ Object { "type": "cases", }, }, - "message": "User is updating cases [id=5] as owner \\"super\\"", + "message": "User is pushing cases [id=5] as owner \\"super\\"", } `; @@ -2516,7 +2516,7 @@ Object { "change", ], }, - "message": "User is updating a case as any owners", + "message": "User is pushing a case as any owners", } `; diff --git a/x-pack/plugins/cases/server/authorization/index.ts b/x-pack/plugins/cases/server/authorization/index.ts index 4bd881364cfc2..12653aa6079e6 100644 --- a/x-pack/plugins/cases/server/authorization/index.ts +++ b/x-pack/plugins/cases/server/authorization/index.ts @@ -39,6 +39,12 @@ const updateVerbs: Verbs = { past: 'updated', }; +const pushVerbs: Verbs = { + present: 'push', + progressive: 'pushing', + past: 'pushed', +}; + const deleteVerbs: Verbs = { present: 'delete', progressive: 'deleting', @@ -164,7 +170,7 @@ const CaseOperations = { ecsType: EVENT_TYPES.change, name: WriteOperations.PushCase as const, action: 'case_push', - verbs: updateVerbs, + verbs: pushVerbs, docType: 'case', savedObjectType: CASE_SAVED_OBJECT, }, diff --git a/x-pack/plugins/cases/server/config.test.ts b/x-pack/plugins/cases/server/config.test.ts index 54fc42f694bc2..352faac983f29 100644 --- a/x-pack/plugins/cases/server/config.test.ts +++ b/x-pack/plugins/cases/server/config.test.ts @@ -106,6 +106,9 @@ describe('config validation', () => { "markdownPlugins": Object { "lens": true, }, + "stack": Object { + "enabled": true, + }, } `); }); diff --git a/x-pack/plugins/cases/server/config.ts b/x-pack/plugins/cases/server/config.ts index c2daeb73b03de..7e30671ee4734 100644 --- a/x-pack/plugins/cases/server/config.ts +++ b/x-pack/plugins/cases/server/config.ts @@ -20,6 +20,9 @@ export const ConfigSchema = schema.object({ // intentionally not setting a default here so that we can determine if the user set it maxSize: schema.maybe(schema.number({ min: 0 })), }), + stack: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), }); export type ConfigType = TypeOf; diff --git a/x-pack/plugins/cases/server/index.ts b/x-pack/plugins/cases/server/index.ts index 62748905f6b82..b1cadb43f147c 100644 --- a/x-pack/plugins/cases/server/index.ts +++ b/x-pack/plugins/cases/server/index.ts @@ -16,6 +16,7 @@ export const config: PluginConfigDescriptor = { exposeToBrowser: { markdownPlugins: true, files: { maxSize: true, allowedMimeTypes: true }, + stack: { enabled: true }, }, deprecations: ({ renameFromRoot }) => [ renameFromRoot('xpack.case.enabled', 'xpack.cases.enabled', { level: 'critical' }), diff --git a/x-pack/plugins/cases/server/plugin.test.ts b/x-pack/plugins/cases/server/plugin.test.ts new file mode 100644 index 0000000000000..255e6ce42af68 --- /dev/null +++ b/x-pack/plugins/cases/server/plugin.test.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { PluginInitializerContext } from '@kbn/core/server'; +import {} from '@kbn/core/server'; +import { coreMock } from '@kbn/core/server/mocks'; +import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/server/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; +import { featuresPluginMock } from '@kbn/features-plugin/server/mocks'; +import { createFilesSetupMock } from '@kbn/files-plugin/server/mocks'; +import { securityMock } from '@kbn/security-plugin/server/mocks'; +import { makeLensEmbeddableFactory } from '@kbn/lens-plugin/server/embeddable/make_lens_embeddable_factory'; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import { actionsMock } from '@kbn/actions-plugin/server/mocks'; +import { notificationsMock } from '@kbn/notifications-plugin/server/mocks'; +import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; +import type { PluginsSetup, PluginsStart } from './plugin'; +import { CasePlugin } from './plugin'; +import type { ConfigType } from './config'; +import { ALLOWED_MIME_TYPES } from '../common/constants/mime_types'; + +function getConfig(overrides = {}) { + return { + markdownPlugins: { lens: true }, + files: { maxSize: 1, allowedMimeTypes: ALLOWED_MIME_TYPES }, + stack: { enabled: true }, + ...overrides, + }; +} + +describe('Cases Plugin', () => { + let context: PluginInitializerContext; + let plugin: CasePlugin; + let coreSetup: ReturnType; + let coreStart: ReturnType; + let pluginsSetup: jest.Mocked; + let pluginsStart: jest.Mocked; + + beforeEach(() => { + context = coreMock.createPluginInitializerContext(getConfig()); + + plugin = new CasePlugin(context); + coreSetup = coreMock.createSetup(); + coreStart = coreMock.createStart(); + + pluginsSetup = { + taskManager: taskManagerMock.createSetup(), + actions: actionsMock.createSetup(), + files: createFilesSetupMock(), + lens: { + lensEmbeddableFactory: makeLensEmbeddableFactory( + () => ({}), + () => ({}), + {} + ), + registerVisualizationMigration: jest.fn(), + }, + security: securityMock.createSetup(), + licensing: licensingMock.createSetup(), + usageCollection: usageCollectionPluginMock.createSetupContract(), + features: featuresPluginMock.createSetup(), + }; + + pluginsStart = { + licensing: licensingMock.createStart(), + actions: actionsMock.createStart(), + files: { fileServiceFactory: { asScoped: jest.fn(), asInternal: jest.fn() } }, + features: featuresPluginMock.createStart(), + security: securityMock.createStart(), + notifications: notificationsMock.createStart(), + ruleRegistry: { getRacClientWithRequest: jest.fn(), alerting: alertsMock.createStart() }, + }; + }); + + describe('setup()', () => { + it('should start setup cases plugin correctly', async () => { + plugin.setup(coreSetup, pluginsSetup); + + expect(context.logger.get().debug).toHaveBeenCalledWith( + `Setting up Case Workflow with core contract [${Object.keys( + coreSetup + )}] and plugins [${Object.keys(pluginsSetup)}]` + ); + }); + + it('should register kibana feature when stack is enabled', async () => { + plugin.setup(coreSetup, pluginsSetup); + + expect(pluginsSetup.features.registerKibanaFeature).toHaveBeenCalled(); + }); + + it('should not register kibana feature when stack is disabled', async () => { + context = coreMock.createPluginInitializerContext( + getConfig({ stack: { enabled: false } }) + ); + const pluginWithStackDisabled = new CasePlugin(context); + + pluginWithStackDisabled.setup(coreSetup, pluginsSetup); + + expect(pluginsSetup.features.registerKibanaFeature).not.toHaveBeenCalled(); + }); + }); + + describe('start', () => { + it('should start cases plugin correctly', async () => { + const pluginStart = plugin.start(coreStart, pluginsStart); + + expect(context.logger.get().debug).toHaveBeenCalledWith(`Starting Case Workflow`); + + expect(pluginStart).toMatchInlineSnapshot(` + Object { + "getCasesClientWithRequest": [Function], + "getExternalReferenceAttachmentTypeRegistry": [Function], + "getPersistableStateAttachmentTypeRegistry": [Function], + } + `); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/plugin.ts b/x-pack/plugins/cases/server/plugin.ts index 0ae005e09b188..510686f1a98bd 100644 --- a/x-pack/plugins/cases/server/plugin.ts +++ b/x-pack/plugins/cases/server/plugin.ts @@ -121,7 +121,9 @@ export class CasePlugin { this.securityPluginSetup = plugins.security; this.lensEmbeddableFactory = plugins.lens.lensEmbeddableFactory; - plugins.features.registerKibanaFeature(getCasesKibanaFeature()); + if (this.caseConfig.stack.enabled) { + plugins.features.registerKibanaFeature(getCasesKibanaFeature()); + } core.savedObjects.registerType( createCaseCommentSavedObjectType({ diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts index 4c28b896bd855..5929e39a2dd32 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts @@ -15,6 +15,9 @@ import { createCasesRoute } from '../create_cases_route'; export const getConnectorsRoute = createCasesRoute({ method: 'get', path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, + routerOptions: { + tags: ['access:casesGetConnectorsConfigure'], + }, handler: async ({ context, response }) => { try { const caseContext = await context.cases; diff --git a/x-pack/plugins/cases/server/routes/api/types.ts b/x-pack/plugins/cases/server/routes/api/types.ts index 3dafad71b3cd8..a24e170ccaa16 100644 --- a/x-pack/plugins/cases/server/routes/api/types.ts +++ b/x-pack/plugins/cases/server/routes/api/types.ts @@ -40,7 +40,7 @@ interface CaseRouteHandlerArguments { kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; } -type CaseRouteTags = 'access:casesSuggestUserProfiles'; +type CaseRouteTags = 'access:casesSuggestUserProfiles' | 'access:casesGetConnectorsConfigure'; export interface CaseRoute

{ method: 'get' | 'post' | 'put' | 'delete' | 'patch'; diff --git a/x-pack/plugins/cases/tsconfig.json b/x-pack/plugins/cases/tsconfig.json index 059e132582d00..ccd6e228d6aff 100644 --- a/x-pack/plugins/cases/tsconfig.json +++ b/x-pack/plugins/cases/tsconfig.json @@ -61,7 +61,6 @@ "@kbn/shared-ux-file-upload", "@kbn/shared-ux-file-mocks", "@kbn/saved-objects-finder-plugin", - "@kbn/saved-objects-management-plugin", "@kbn/utility-types-jest", "@kbn/ui-actions-plugin", "@kbn/core-lifecycle-browser", @@ -69,6 +68,8 @@ "@kbn/core-theme-browser", "@kbn/serverless", "@kbn/core-http-server", + "@kbn/alerting-plugin", + "@kbn/content-management-plugin", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cloud_integrations/cloud_links/kibana.jsonc b/x-pack/plugins/cloud_integrations/cloud_links/kibana.jsonc index b9b9a0f629b64..4b6625f842f79 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/kibana.jsonc +++ b/x-pack/plugins/cloud_integrations/cloud_links/kibana.jsonc @@ -11,6 +11,9 @@ "cloud", "security", "guidedOnboarding" + ], + "requiredBundles": [ + "kibanaReact" ] } } diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/__snapshots__/maybe_add_cloud_links.test.ts.snap b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/__snapshots__/maybe_add_cloud_links.test.ts.snap new file mode 100644 index 0000000000000..7a957943fe2f0 --- /dev/null +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/__snapshots__/maybe_add_cloud_links.test.ts.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`maybeAddCloudLinks when cloud enabled and it fails to fetch the user, it sets the links 2`] = ` +Array [ + Object { + "href": "profile-url", + "iconType": "user", + "label": "Profile", + "order": 100, + "setAsProfile": true, + }, + Object { + "href": "billing-url", + "iconType": "visGauge", + "label": "Billing", + "order": 200, + }, + Object { + "href": "organization-url", + "iconType": "gear", + "label": "Organization", + "order": 300, + }, + Any, +] +`; + +exports[`maybeAddCloudLinks when cloud enabled and the user is an Elastic Cloud user, it sets the links 2`] = ` +Array [ + Object { + "href": "profile-url", + "iconType": "user", + "label": "Profile", + "order": 100, + "setAsProfile": true, + }, + Object { + "href": "billing-url", + "iconType": "visGauge", + "label": "Billing", + "order": 200, + }, + Object { + "href": "organization-url", + "iconType": "gear", + "label": "Organization", + "order": 300, + }, + Any, +] +`; diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts index f2e14d1a08526..b9045fdc9a59f 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts @@ -14,12 +14,11 @@ import { maybeAddCloudLinks } from './maybe_add_cloud_links'; describe('maybeAddCloudLinks', () => { it('should skip if cloud is disabled', async () => { const security = securityMock.createStart(); + const core = coreMock.createStart(); maybeAddCloudLinks({ + core, security, - chrome: coreMock.createStart().chrome, cloud: { ...cloudMock.createStart(), isCloudEnabled: false }, - docLinks: coreMock.createStart().docLinks, - uiSettingsClient: coreMock.createStart().uiSettings, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); @@ -31,13 +30,12 @@ describe('maybeAddCloudLinks', () => { security.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ elastic_cloud_user: true }) ); - const { chrome, docLinks, uiSettings } = coreMock.createStart(); + const core = coreMock.createStart(); + const { chrome } = core; maybeAddCloudLinks({ security, - chrome, + core, cloud: { ...cloudMock.createStart(), isCloudEnabled: true }, - docLinks, - uiSettingsClient: uiSettings, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); @@ -53,104 +51,28 @@ describe('maybeAddCloudLinks', () => { ] `); expect(security.navControlService.addUserMenuLinks).toHaveBeenCalledTimes(1); - expect(security.navControlService.addUserMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "href": "profile-url", - "iconType": "user", - "label": "Profile", - "order": 100, - "setAsProfile": true, - }, - Object { - "href": "billing-url", - "iconType": "visGauge", - "label": "Billing", - "order": 200, - }, - Object { - "href": "organization-url", - "iconType": "gear", - "label": "Organization", - "order": 300, - }, - Object { - "content": , - "href": "", - "iconType": "", - "label": "", - "order": 400, - }, - ], - ] - `); + expect(security.navControlService.addUserMenuLinks.mock.calls[0][0]).toMatchSnapshot([ + { + href: 'profile-url', + iconType: 'user', + label: 'Profile', + order: 100, + setAsProfile: true, + }, + { + href: 'billing-url', + iconType: 'visGauge', + label: 'Billing', + order: 200, + }, + { + href: 'organization-url', + iconType: 'gear', + label: 'Organization', + order: 300, + }, + expect.any(Object), + ]); expect(chrome.setHelpMenuLinks).toHaveBeenCalledTimes(1); expect(chrome.setHelpMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` @@ -176,13 +98,12 @@ describe('maybeAddCloudLinks', () => { it('when cloud enabled and it fails to fetch the user, it sets the links', async () => { const security = securityMock.createStart(); security.authc.getCurrentUser.mockRejectedValue(new Error('Something went terribly wrong')); - const { chrome, docLinks, uiSettings } = coreMock.createStart(); + const core = coreMock.createStart(); + const { chrome } = core; maybeAddCloudLinks({ security, - chrome, + core, cloud: { ...cloudMock.createStart(), isCloudEnabled: true }, - docLinks, - uiSettingsClient: uiSettings, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); @@ -198,104 +119,28 @@ describe('maybeAddCloudLinks', () => { ] `); expect(security.navControlService.addUserMenuLinks).toHaveBeenCalledTimes(1); - expect(security.navControlService.addUserMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "href": "profile-url", - "iconType": "user", - "label": "Profile", - "order": 100, - "setAsProfile": true, - }, - Object { - "href": "billing-url", - "iconType": "visGauge", - "label": "Billing", - "order": 200, - }, - Object { - "href": "organization-url", - "iconType": "gear", - "label": "Organization", - "order": 300, - }, - Object { - "content": , - "href": "", - "iconType": "", - "label": "", - "order": 400, - }, - ], - ] - `); + expect(security.navControlService.addUserMenuLinks.mock.calls[0][0]).toMatchSnapshot([ + { + href: 'profile-url', + iconType: 'user', + label: 'Profile', + order: 100, + setAsProfile: true, + }, + { + href: 'billing-url', + iconType: 'visGauge', + label: 'Billing', + order: 200, + }, + { + href: 'organization-url', + iconType: 'gear', + label: 'Organization', + order: 300, + }, + expect.any(Object), + ]); expect(chrome.setHelpMenuLinks).toHaveBeenCalledTimes(1); expect(chrome.setHelpMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` Array [ @@ -322,13 +167,12 @@ describe('maybeAddCloudLinks', () => { security.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ elastic_cloud_user: false }) ); - const { chrome, docLinks, uiSettings } = coreMock.createStart(); + const core = coreMock.createStart(); + const { chrome } = core; maybeAddCloudLinks({ security, - chrome, + core, cloud: { ...cloudMock.createStart(), isCloudEnabled: true }, - docLinks, - uiSettingsClient: uiSettings, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts index 1becd2cdf7254..33fb4df7bfce2 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts @@ -9,28 +9,18 @@ import { catchError, defer, filter, map, of, combineLatest } from 'rxjs'; import { i18n } from '@kbn/i18n'; import type { CloudStart } from '@kbn/cloud-plugin/public'; -import type { ChromeStart } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; import type { SecurityPluginStart } from '@kbn/security-plugin/public'; -import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; -import type { DocLinksStart } from '@kbn/core-doc-links-browser'; import { createUserMenuLinks } from './user_menu_links'; import { createHelpMenuLinks } from './help_menu_links'; export interface MaybeAddCloudLinksDeps { + core: CoreStart; security: SecurityPluginStart; - chrome: ChromeStart; cloud: CloudStart; - docLinks: DocLinksStart; - uiSettingsClient: IUiSettingsClient; } -export function maybeAddCloudLinks({ - security, - chrome, - cloud, - docLinks, - uiSettingsClient, -}: MaybeAddCloudLinksDeps): void { +export function maybeAddCloudLinks({ core, security, cloud }: MaybeAddCloudLinksDeps): void { const userObservable = defer(() => security.authc.getCurrentUser()).pipe( // Check if user is a cloud user. map((user) => user.elastic_cloud_user), @@ -39,7 +29,7 @@ export function maybeAddCloudLinks({ filter((isElasticCloudUser) => isElasticCloudUser === true), map(() => { if (cloud.deploymentUrl) { - chrome.setCustomNavLink({ + core.chrome.setCustomNavLink({ title: i18n.translate('xpack.cloudLinks.deploymentLinkLabel', { defaultMessage: 'Manage this deployment', }), @@ -47,22 +37,26 @@ export function maybeAddCloudLinks({ href: cloud.deploymentUrl, }); } - const userMenuLinks = createUserMenuLinks({ cloud, security, uiSettingsClient }); + const userMenuLinks = createUserMenuLinks({ + core, + cloud, + security, + }); security.navControlService.addUserMenuLinks(userMenuLinks); }) ); - const helpObservable = chrome.getHelpSupportUrl$(); + const helpObservable = core.chrome.getHelpSupportUrl$(); if (cloud.isCloudEnabled) { combineLatest({ user: userObservable, helpSupportUrl: helpObservable }).subscribe( ({ helpSupportUrl }) => { const helpMenuLinks = createHelpMenuLinks({ - docLinks, + docLinks: core.docLinks, helpSupportUrl, }); - chrome.setHelpMenuLinks(helpMenuLinks); + core.chrome.setHelpMenuLinks(helpMenuLinks); } ); } diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts index 75d6ae4d1d329..c0a4fcc3a09ac 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts @@ -7,21 +7,20 @@ import { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import type { SecurityPluginStart } from '@kbn/security-plugin/public'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { useUpdateUserProfile } from '@kbn/user-profile-components'; interface Deps { uiSettingsClient: IUiSettingsClient; - security: SecurityPluginStart; } -export const useThemeDarkmodeToggle = ({ uiSettingsClient, security }: Deps) => { +export const useThemeDarkmodeToggle = ({ uiSettingsClient }: Deps) => { const [isDarkModeOn, setIsDarkModeOn] = useState(false); // If a value is set in kibana.yml (uiSettings.overrides.theme:darkMode) // we don't allow the user to change the theme color. const valueSetInKibanaConfig = uiSettingsClient.isOverridden('theme:darkMode'); - const { userProfileData, isLoading, update } = security.hooks.useUpdateUserProfile({ + const { userProfileData, isLoading, update } = useUpdateUserProfile({ notificationSuccess: { title: i18n.translate('xpack.cloudLinks.userMenuLinks.darkMode.successNotificationTitle', { defaultMessage: 'Color theme updated', diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.test.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.test.tsx index 2bebdad488498..6b06cd64b9e23 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.test.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.test.tsx @@ -11,18 +11,22 @@ import '@testing-library/jest-dom'; import { coreMock } from '@kbn/core/public/mocks'; import { securityMock } from '@kbn/security-plugin/public/mocks'; -import { ThemDarkModeToggle } from './theme_darkmode_toggle'; +import { ThemeDarkModeToggle } from './theme_darkmode_toggle'; -describe('ThemDarkModeToggle', () => { - const mockUseUpdateUserProfile = jest.fn(); - const mockGetSpaceDarkModeValue = jest.fn(); +const mockUseUpdateUserProfile = jest.fn(); +jest.mock('@kbn/user-profile-components', () => { + const original = jest.requireActual('@kbn/user-profile-components'); + return { + ...original, + useUpdateUserProfile: () => mockUseUpdateUserProfile(), + }; +}); + +describe('ThemeDarkModeToggle', () => { it('renders correctly and toggles dark mode', () => { - const security = { - ...securityMock.createStart(), - hooks: { useUpdateUserProfile: mockUseUpdateUserProfile }, - }; - const { uiSettings } = coreMock.createStart(); + const security = securityMock.createStart(); + const core = coreMock.createStart(); const mockUpdate = jest.fn(); mockUseUpdateUserProfile.mockReturnValue({ @@ -31,10 +35,8 @@ describe('ThemDarkModeToggle', () => { update: mockUpdate, }); - mockGetSpaceDarkModeValue.mockReturnValue(false); - const { getByTestId, rerender } = render( - + ); const toggleSwitch = getByTestId('darkModeToggleSwitch'); @@ -49,7 +51,7 @@ describe('ThemDarkModeToggle', () => { }); // Rerender the component to apply the new props - rerender(); + rerender(); fireEvent.click(toggleSwitch); expect(mockUpdate).toHaveBeenLastCalledWith({ userSettings: { darkMode: 'light' } }); diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx index 3f85c2ed63a76..20085172b2c68 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_toggle.tsx @@ -17,19 +17,30 @@ import { import { i18n } from '@kbn/i18n'; import type { SecurityPluginStart } from '@kbn/security-plugin/public'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { UserProfilesKibanaProvider } from '@kbn/user-profile-components'; +import { CoreStart } from '@kbn/core-lifecycle-browser'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; + import { useThemeDarkmodeToggle } from './theme_darkmode_hook'; interface Props { - uiSettingsClient: IUiSettingsClient; security: SecurityPluginStart; + core: CoreStart; } -export const ThemDarkModeToggle = ({ security, uiSettingsClient }: Props) => { +export const ThemeDarkModeToggle = ({ security, core }: Props) => { + return ( + + + + ); +}; + +function ThemeDarkModeToggleUi({ uiSettingsClient }: { uiSettingsClient: IUiSettingsClient }) { const toggleTextSwitchId = useGeneratedHtmlId({ prefix: 'toggleTextSwitch' }); const { euiTheme } = useEuiTheme(); const { isVisible, toggle, isDarkModeOn, colorScheme } = useThemeDarkmodeToggle({ - security, uiSettingsClient, }); @@ -77,4 +88,4 @@ export const ThemDarkModeToggle = ({ security, uiSettingsClient }: Props) => { ); -}; +} diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.tsx index 1eae5d6ed0c58..16ffa32360f25 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.tsx @@ -9,17 +9,17 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { SecurityPluginStart, UserMenuLink } from '@kbn/security-plugin/public'; -import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; -import { ThemDarkModeToggle } from './theme_darkmode_toggle'; +import type { CoreStart } from '@kbn/core/public'; +import { ThemeDarkModeToggle } from './theme_darkmode_toggle'; export const createUserMenuLinks = ({ + core, cloud, security, - uiSettingsClient, }: { + core: CoreStart; cloud: CloudStart; security: SecurityPluginStart; - uiSettingsClient: IUiSettingsClient; }): UserMenuLink[] => { const { profileUrl, billingUrl, organizationUrl } = cloud; @@ -60,7 +60,7 @@ export const createUserMenuLinks = ({ } userMenuLinks.push({ - content: , + content: , order: 400, label: '', iconType: '', diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx index 7ee3f0969251d..38b568791b70b 100755 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx @@ -44,11 +44,9 @@ export class CloudLinksPlugin } if (security) { maybeAddCloudLinks({ + core, security, - chrome: core.chrome, cloud, - docLinks: core.docLinks, - uiSettingsClient: core.uiSettings, }); } } diff --git a/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json b/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json index 0dfa7ce42858d..f1a67895cdd5e 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json +++ b/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json @@ -20,6 +20,9 @@ "@kbn/core-chrome-browser", "@kbn/core-doc-links-browser", "@kbn/core-ui-settings-browser", + "@kbn/user-profile-components", + "@kbn/core-lifecycle-browser", + "@kbn/kibana-react-plugin", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 91d42c2544191..058b23e3477a5 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -123,5 +123,3 @@ export const VULNERABILITIES_SEVERITY: Record = { CRITICAL: 'CRITICAL', UNKNOWN: 'UNKNOWN', }; - -export const VULNERABILITIES_ENUMERATION = 'CVE'; diff --git a/x-pack/plugins/cloud_security_posture/common/utils/get_safe_vulnerabilities_query_filter.ts b/x-pack/plugins/cloud_security_posture/common/utils/get_safe_vulnerabilities_query_filter.ts index 5cbbf6dd054a7..fb2bbd1c51273 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/get_safe_vulnerabilities_query_filter.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/get_safe_vulnerabilities_query_filter.ts @@ -5,7 +5,7 @@ * 2.0. */ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { VULNERABILITIES_ENUMERATION, VULNERABILITIES_SEVERITY } from '../constants'; +import { VULNERABILITIES_SEVERITY } from '../constants'; export const getSafeVulnerabilitiesQueryFilter = (query?: QueryDslQueryContainer) => ({ ...query, @@ -29,7 +29,6 @@ export const getSafeVulnerabilitiesQueryFilter = (query?: QueryDslQueryContainer { exists: { field: 'vulnerability.severity' } }, { exists: { field: 'resource.id' } }, { exists: { field: 'resource.name' } }, - { match_phrase: { 'vulnerability.enumeration': VULNERABILITIES_ENUMERATION } }, ], }, }); diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/create_detection_rule.ts b/x-pack/plugins/cloud_security_posture/public/common/api/create_detection_rule.ts new file mode 100644 index 0000000000000..ef0aa3321f35e --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/api/create_detection_rule.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { HttpSetup } from '@kbn/core/public'; + +const DETECTION_ENGINE_URL = '/api/detection_engine' as const; +const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules` as const; + +interface RuleCreateProps { + type: string; + language: string; + license: string; + author: string[]; + filters: any[]; + false_positives: any[]; + risk_score: number; + risk_score_mapping: any[]; + severity: string; + severity_mapping: any[]; + threat: any[]; + interval: string; + from: string; + to: string; + timestamp_override: string; + timestamp_override_fallback_disabled: boolean; + actions: any[]; + enabled: boolean; + alert_suppression: { + group_by: string[]; + missing_fields_strategy: string; + }; + index: string[]; + query: string; + references: string[]; + name: string; + description: string; + tags: string[]; +} + +export interface RuleResponse extends RuleCreateProps { + id: string; +} + +export const createDetectionRule = async ({ + http, + rule, +}: { + http: HttpSetup; + rule: RuleCreateProps; +}): Promise => { + const res = await http.post(DETECTION_ENGINE_RULES_URL, { + body: JSON.stringify(rule), + }); + + return res as RuleResponse; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/take_action.tsx b/x-pack/plugins/cloud_security_posture/public/components/take_action.tsx new file mode 100644 index 0000000000000..57684d02fd157 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/take_action.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { + EuiButton, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiText, + useGeneratedHtmlId, +} from '@elastic/eui'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import type { HttpSetup } from '@kbn/core/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { CREATE_RULE_ACTION_SUBJ, TAKE_ACTION_SUBJ } from './test_subjects'; +import { useKibana } from '../common/hooks/use_kibana'; +import type { RuleResponse } from '../common/api/create_detection_rule'; + +const RULE_PAGE_PATH = '/app/security/rules/id/'; + +interface TakeActionProps { + createRuleFn: (http: HttpSetup) => Promise; +} +/* + * This component is used to create a detection rule from Flyout. + * It accepts a createRuleFn parameter which is used to create a rule in a generic way. + */ +export const TakeAction = ({ createRuleFn }: TakeActionProps) => { + const [isPopoverOpen, setPopoverOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const closePopover = () => { + setPopoverOpen(false); + }; + + const smallContextMenuPopoverId = useGeneratedHtmlId({ + prefix: 'smallContextMenuPopover', + }); + + const { http, notifications } = useKibana().services; + + const showSuccessToast = (ruleResponse: RuleResponse) => { + return notifications.toasts.addSuccess({ + toastLifeTimeMs: 10000, + color: 'success', + iconType: '', + text: toMountPoint( +
+ + {ruleResponse.name} + {` `} + + + + + + + + + + + + +
+ ), + }); + }; + + const button = ( + setPopoverOpen(!isPopoverOpen)} + > + + + ); + + return ( + + { + closePopover(); + setIsLoading(true); + const ruleResponse = await createRuleFn(http); + setIsLoading(false); + showSuccessToast(ruleResponse); + }} + data-test-subj={CREATE_RULE_ACTION_SUBJ} + > + + , + ]} + /> + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts index 638076818d3f0..a1fa5d985df3c 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts @@ -32,4 +32,7 @@ export const NO_VULNERABILITIES_STATUS_TEST_SUBJ = { export const VULNERABILITIES_CONTAINER_TEST_SUBJ = 'vulnerabilities_container'; -export const VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ = 'vuknerabilities_cvss_score_badge'; +export const VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ = 'vulnerabilities_cvss_score_badge'; + +export const TAKE_ACTION_SUBJ = 'csp:take_action'; +export const CREATE_RULE_ACTION_SUBJ = 'csp:create_rule'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx index f59463e00125f..2c59f360850d8 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx @@ -25,9 +25,11 @@ import { } from '@elastic/eui'; import { assertNever } from '@kbn/std'; import { i18n } from '@kbn/i18n'; +import type { HttpSetup } from '@kbn/core/public'; import cisLogoIcon from '../../../assets/icons/cis_logo.svg'; import { CspFinding } from '../../../../common/schemas/csp_finding'; import { CspEvaluationBadge } from '../../../components/csp_evaluation_badge'; +import { TakeAction } from '../../../components/take_action'; import { TableTab } from './table_tab'; import { JsonTab } from './json_tab'; import { OverviewTab } from './overview_tab'; @@ -36,6 +38,7 @@ import type { BenchmarkId } from '../../../../common/types'; import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon'; import { BenchmarkName } from '../../../../common/types'; import { FINDINGS_FLYOUT } from '../test_subjects'; +import { createDetectionRuleFromFinding } from '../utils/create_detection_rule_from_finding'; const tabs = [ { @@ -127,6 +130,9 @@ export const FindingsRuleFlyout = ({ }: FindingFlyoutProps) => { const [tab, setTab] = useState(tabs[0]); + const createMisconfigurationRuleFn = async (http: HttpSetup) => + await createDetectionRuleFromFinding(http, findings); + return ( @@ -160,7 +166,7 @@ export const FindingsRuleFlyout = ({ - + + + + diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_finding.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_finding.ts new file mode 100644 index 0000000000000..179ac6e27713c --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_finding.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpSetup } from '@kbn/core/public'; +import type { CspFinding } from '../../../../common/schemas/csp_finding'; +import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants'; +import { createDetectionRule } from '../../../common/api/create_detection_rule'; + +const DEFAULT_RULE_RISK_SCORE = 0; +const DEFAULT_RULE_SEVERITY = 'low'; +const DEFAULT_RULE_ENABLED = true; +const DEFAULT_RULE_AUTHOR = 'Elastic'; +const DEFAULT_RULE_LICENSE = 'Elastic License v2'; +const ALERT_SUPPRESSION_FIELD = 'resource.id'; +const ALERT_TIMESTAMP_FIELD = 'event.ingested'; + +enum AlertSuppressionMissingFieldsStrategy { + // per each document a separate alert will be created + DoNotSuppress = 'doNotSuppress', + // only one alert will be created per suppress by bucket + Suppress = 'suppress', +} + +const convertReferencesLinksToArray = (input: string | undefined) => { + if (!input) { + return []; + } + // Match all URLs in the input string using a regular expression + const matches = input.match(/(https?:\/\/\S+)/g); + + if (!matches) { + return []; + } + + // Remove the numbers and new lines + return matches.map((link) => link.replace(/^\d+\. /, '').replace(/\n/g, '')); +}; + +const STATIC_RULE_TAGS = ['Elastic', 'Cloud Security']; + +const generateMisconfigurationsTags = (finding: CspFinding) => { + return [STATIC_RULE_TAGS] + .concat(finding.rule.tags) + .concat( + finding.rule.benchmark.posture_type ? [finding.rule.benchmark.posture_type.toUpperCase()] : [] + ) + .flat(); +}; + +const generateMisconfigurationsRuleQuery = (finding: CspFinding) => { + return ` + rule.benchmark.rule_number: "${finding.rule.benchmark.rule_number}" + AND rule.benchmark.id: "${finding.rule.benchmark.id}" + AND result.evaluation: "failed" + `; +}; + +/* + * Creates a detection rule from a CspFinding + */ +export const createDetectionRuleFromFinding = async (http: HttpSetup, finding: CspFinding) => { + return await createDetectionRule({ + http, + rule: { + type: 'query', + language: 'kuery', + license: DEFAULT_RULE_LICENSE, + author: [DEFAULT_RULE_AUTHOR], + filters: [], + false_positives: [], + risk_score: DEFAULT_RULE_RISK_SCORE, + risk_score_mapping: [], + severity: DEFAULT_RULE_SEVERITY, + severity_mapping: [], + threat: [], + interval: '1h', + from: 'now-7200s', + to: 'now', + timestamp_override: ALERT_TIMESTAMP_FIELD, + timestamp_override_fallback_disabled: false, + actions: [], + enabled: DEFAULT_RULE_ENABLED, + alert_suppression: { + group_by: [ALERT_SUPPRESSION_FIELD], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.Suppress, + }, + index: [LATEST_FINDINGS_INDEX_DEFAULT_NS], + query: generateMisconfigurationsRuleQuery(finding), + references: convertReferencesLinksToArray(finding.rule.references), + name: finding.rule.name, + description: finding.rule.rationale, + tags: generateMisconfigurationsTags(finding), + }, + }); +}; diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index ae8f7d610002b..a88bbf2bd0995 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -48,7 +48,7 @@ "@kbn/shared-ux-router", "@kbn/core-saved-objects-server", "@kbn/share-plugin", - "@kbn/core-http-server", + "@kbn/core-http-server" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx index 9e71561f051a3..31e18d155b72d 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx @@ -22,17 +22,14 @@ import { import moment from 'moment'; import { IUiSettingsClient } from '@kbn/core/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; +import type { LogRateHistogramItem } from '@kbn/aiops-utils'; + import { EuiFlexGroup, EuiLoadingSpinner, EuiFlexItem } from '@elastic/eui'; import { useDataVisualizerKibana } from '../../../../kibana_context'; -export interface DocumentCountChartPoint { - time: number | string; - value: number; -} - interface Props { width?: number; - chartPoints: DocumentCountChartPoint[]; + chartPoints: LogRateHistogramItem[]; timeRangeEarliest: number; timeRangeLatest: number; interval?: number; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/index.ts b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/index.ts index 1b69f9b825165..870cb010d2038 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/index.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/index.ts @@ -5,5 +5,4 @@ * 2.0. */ -export type { DocumentCountChartPoint } from './document_count_chart'; export { DocumentCountChart } from './document_count_chart'; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_content.tsx index 911a6ba9ea731..3d1268140df17 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_content.tsx @@ -20,7 +20,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { DocumentCountChartPoint } from './document_count_chart'; +import type { LogRateHistogramItem } from '@kbn/aiops-utils'; import { RandomSamplerOption, RANDOM_SAMPLER_SELECT_OPTIONS, @@ -108,7 +108,7 @@ export const DocumentCountContent: FC = ({ if (timeRangeEarliest === undefined || timeRangeLatest === undefined) return ; - let chartPoints: DocumentCountChartPoint[] = []; + let chartPoints: LogRateHistogramItem[] = []; if (documentCountStats.buckets !== undefined) { const buckets: Record = documentCountStats?.buckets; chartPoints = Object.entries(buckets).map(([time, value]) => ({ time: +time, value })); diff --git a/x-pack/plugins/data_visualizer/public/application/data_comparison/document_count_with_dual_brush.tsx b/x-pack/plugins/data_visualizer/public/application/data_comparison/document_count_with_dual_brush.tsx index cc4267be0087b..159a1a52597ca 100644 --- a/x-pack/plugins/data_visualizer/public/application/data_comparison/document_count_with_dual_brush.tsx +++ b/x-pack/plugins/data_visualizer/public/application/data_comparison/document_count_with_dual_brush.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import { WindowParameters } from '@kbn/aiops-utils'; +import type { WindowParameters, LogRateHistogramItem } from '@kbn/aiops-utils'; import React, { FC } from 'react'; -import { DocumentCountChart, type DocumentCountChartPoint } from '@kbn/aiops-components'; +import { DocumentCountChart } from '@kbn/aiops-components'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { DocumentCountChartProps } from '@kbn/aiops-components'; +import type { BrushSelectionUpdateHandler, DocumentCountChartProps } from '@kbn/aiops-components'; import { RandomSampler } from '@kbn/ml-random-sampler-utils'; import { useDataVisualizerKibana } from '../kibana_context'; import { DocumentCountStats } from '../../../common/types/field_stats'; @@ -26,7 +26,7 @@ export interface DocumentCountContentProps | 'interval' | 'chartPointsSplitLabel' > { - brushSelectionUpdateHandler: (d: WindowParameters, force: boolean) => void; + brushSelectionUpdateHandler: BrushSelectionUpdateHandler; documentCountStats?: DocumentCountStats; documentCountStatsSplit?: DocumentCountStats; documentCountStatsSplitLabel?: string; @@ -83,14 +83,14 @@ export const DocumentCountWithDualBrush: FC = ({ return totalCount !== undefined ? : null; } - const chartPoints: DocumentCountChartPoint[] = Object.entries(documentCountStats.buckets).map( + const chartPoints: LogRateHistogramItem[] = Object.entries(documentCountStats.buckets).map( ([time, value]) => ({ time: +time, value, }) ); - let chartPointsSplit: DocumentCountChartPoint[] | undefined; + let chartPointsSplit: LogRateHistogramItem[] | undefined; if (documentCountStatsSplit?.buckets !== undefined) { chartPointsSplit = Object.entries(documentCountStatsSplit?.buckets).map(([time, value]) => ({ time: +time, diff --git a/x-pack/plugins/enterprise_search/common/connectors/connectors.ts b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts index 1c5a3f30dcc4a..a1d5b87d9f965 100644 --- a/x-pack/plugins/enterprise_search/common/connectors/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts @@ -150,6 +150,17 @@ export const CONNECTOR_DEFINITIONS: ConnectorServerSideDefinition[] = [ }), serviceType: 'dropbox', }, + { + iconPath: 'gmail.svg', + isBeta: false, + isNative: false, + isTechPreview: true, + keywords: ['google', 'gmail', 'connector', 'mail'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.gmail.name', { + defaultMessage: 'Gmail', + }), + serviceType: 'gmail', + }, { iconPath: 'oracle.svg', isBeta: true, @@ -181,6 +192,17 @@ export const CONNECTOR_DEFINITIONS: ConnectorServerSideDefinition[] = [ }), serviceType: 'servicenow', }, + { + iconPath: 'slack.svg', + isBeta: false, + isNative: false, + isTechPreview: true, + keywords: ['slack', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.slack.name', { + defaultMessage: 'Slack', + }), + serviceType: 'slack', + }, { iconPath: 'sharepoint_server.svg', isBeta: true, diff --git a/x-pack/plugins/enterprise_search/jest.config.dev.js b/x-pack/plugins/enterprise_search/jest.config.dev.js new file mode 100644 index 0000000000000..638235a6deef5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/jest.config.dev.js @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../', + projects: [ + '/x-pack/plugins/enterprise_search/public/**/jest.config.js', + '/x-pack/plugins/enterprise_search/common/**/jest.config.js', + '/x-pack/plugins/enterprise_search/server/**/jest.config.js', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/analytics/jest.config.js new file mode 100644 index 0000000000000..58b5334165ec5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/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/enterprise_search/public/applications/analytics'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/analytics', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_section.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_section.tsx index 1bf181c44d708..4be1cfb5ef9f3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_section.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_section.tsx @@ -11,7 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageSection, EuiSpacer, EuiText, EuiTitle, @@ -19,9 +19,9 @@ import { } from '@elastic/eui'; interface Props { - title: string; - subtitle: string; iconType?: IconType; + subtitle: string; + title: string; } export const AnalyticsSection: React.FC = ({ title, subtitle, iconType, children }) => (
@@ -49,6 +49,6 @@ export const AnalyticsSection: React.FC = ({ title, subtitle, iconType, c - {children} + {children}
); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx index 1cf22f472fc4c..9b85406225b76 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials.tsx @@ -16,8 +16,7 @@ import { EuiButtonIcon, EuiSpacer, EuiButton, - EuiPageContentHeader_Deprecated as EuiPageContentHeader, - EuiPageContentHeaderSection_Deprecated as EuiPageContentHeaderSection, + EuiPageHeader, EuiSkeletonText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -85,31 +84,27 @@ export const Credentials: React.FC = () => { - - - -

- {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.apiKeys', { - defaultMessage: 'API keys', - })} -

-
-
- - {!dataLoading && ( - showCredentialsForm()} - > - {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.createKey', { - defaultMessage: 'Create key', - })} - - )} - -
+ + +

+ {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.apiKeys', { + defaultMessage: 'API keys', + })} +

+
+ {!dataLoading && ( + showCredentialsForm()} + > + {i18n.translate('xpack.enterpriseSearch.appSearch.credentials.createKey', { + defaultMessage: 'Create key', + })} + + )} +
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx index b9d571cc315f6..24ab03d5e93a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx @@ -13,13 +13,13 @@ import { EuiSpacer, EuiPageHeader, EuiTitle, - EuiPageContentBody_Deprecated as EuiPageContentBody, - EuiPageContent_Deprecated as EuiPageContent, + EuiPageSection, EuiDragDropContext, EuiDroppable, EuiDraggable, EuiButtonIconProps, EuiEmptyPrompt, + EuiPageBody, } from '@elastic/eui'; import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; @@ -167,8 +167,8 @@ export const Library: React.FC = () => { <> - - + +

Result

@@ -540,8 +540,8 @@ export const Library: React.FC = () => { -
-
+ + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/app_search/jest.config.js new file mode 100644 index 0000000000000..7d591c369c18b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/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/enterprise_search/public/applications/app_search'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/app_search', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/applications/jest.config.js new file mode 100644 index 0000000000000..1e04c0845ec9f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/applications/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/enterprise_search/public/applications/applications'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/applications', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/jest.config.js new file mode 100644 index 0000000000000..ab90da605f2b3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/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/enterprise_search/public/applications/elasticsearch'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/elasticsearch', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts index 24b9d342f52d4..bea446bf528c0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts @@ -37,6 +37,12 @@ export const CONNECTORS_DICT: Record = { externalDocsUrl: '', icon: CONNECTOR_ICONS.dropbox, }, + gmail: { + docsUrl: '', // TODO + externalAuthDocsUrl: '', + externalDocsUrl: '', + icon: CONNECTOR_ICONS.gmail, + }, google_cloud_storage: { docsUrl: docLinks.connectorsGoogleCloudStorage, externalAuthDocsUrl: 'https://cloud.google.com/storage/docs/authentication', @@ -118,6 +124,13 @@ export const CONNECTORS_DICT: Record = { icon: CONNECTOR_ICONS.sharepoint_online, platinumOnly: true, }, + slack: { + docsUrl: '', // TODO + externalAuthDocsUrl: '', + externalDocsUrl: '', + icon: CONNECTOR_ICONS.slack, + platinumOnly: true, + }, }; export const CONNECTORS = CONNECTOR_DEFINITIONS.map((connector) => ({ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.test.tsx new file mode 100644 index 0000000000000..356de3acc9dc6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiButton } from '@elastic/eui'; + +import { DeployModel } from './deploy_model'; +import { TextExpansionDismissButton } from './text_expansion_callout'; + +const DEFAULT_VALUES = { + startTextExpansionModelError: undefined, + isCreateButtonDisabled: false, + isModelDownloadInProgress: false, + isModelDownloaded: false, + isModelStarted: false, + isStartButtonDisabled: false, +}; + +describe('DeployModel', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(DEFAULT_VALUES); + }); + it('renders deploy button', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isCreateButtonDisabled={false} + isDismissable={false} + /> + ); + expect(wrapper.find(EuiButton).length).toBe(1); + const button = wrapper.find(EuiButton); + expect(button.prop('disabled')).toBe(false); + }); + it('renders disabled deploy button if it is set to disabled', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isCreateButtonDisabled + isDismissable={false} + /> + ); + expect(wrapper.find(EuiButton).length).toBe(1); + const button = wrapper.find(EuiButton); + expect(button.prop('disabled')).toBe(true); + }); + it('renders dismiss button if it is set to dismissable', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isCreateButtonDisabled={false} + isDismissable + /> + ); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); + }); + it('does not render dismiss button if it is set to non-dismissable', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isCreateButtonDisabled={false} + isDismissable={false} + /> + ); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.tsx new file mode 100644 index 0000000000000..40a0059b55eae --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.tsx @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { useActions } from 'kea'; + +import { + EuiBadge, + EuiButton, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, FormattedHTMLMessage } from '@kbn/i18n-react'; + +import { docLinks } from '../../../../../shared/doc_links'; + +import { TextExpansionCallOutState, TextExpansionDismissButton } from './text_expansion_callout'; +import { TextExpansionCalloutLogic } from './text_expansion_callout_logic'; + +export const DeployModel = ({ + dismiss, + ingestionMethod, + isCreateButtonDisabled, + isDismissable, +}: Pick< + TextExpansionCallOutState, + 'dismiss' | 'ingestionMethod' | 'isCreateButtonDisabled' | 'isDismissable' +>) => { + const { createTextExpansionModel } = useActions(TextExpansionCalloutLogic); + + return ( + + + + + + + + + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.title', + { defaultMessage: 'Improve your results with ELSER' } + )} +

+
+
+ {isDismissable && ( + + + + )} +
+
+ + + + + + + + + + + createTextExpansionModel(undefined)} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.deployButton.label', + { + defaultMessage: 'Deploy', + } + )} + + + + + + + + + + + +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.test.tsx new file mode 100644 index 0000000000000..a17eae3ef75f6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiButton } from '@elastic/eui'; + +import { ModelDeployed } from './model_deployed'; +import { TextExpansionDismissButton } from './text_expansion_callout'; + +const DEFAULT_VALUES = { + startTextExpansionModelError: undefined, + isCreateButtonDisabled: false, + isModelDownloadInProgress: false, + isModelDownloaded: false, + isModelStarted: false, + isStartButtonDisabled: false, +}; + +describe('ModelDeployed', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(DEFAULT_VALUES); + }); + it('renders start button', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isDismissable={false} + isStartButtonDisabled={false} + /> + ); + expect(wrapper.find(EuiButton).length).toBe(1); + const button = wrapper.find(EuiButton); + expect(button.prop('disabled')).toBe(false); + }); + it('renders disabled start button if it is set to disabled', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isDismissable={false} + isStartButtonDisabled + /> + ); + expect(wrapper.find(EuiButton).length).toBe(1); + const button = wrapper.find(EuiButton); + expect(button.prop('disabled')).toBe(true); + }); + it('renders dismiss button if it is set to dismissable', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isDismissable + isStartButtonDisabled={false} + /> + ); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); + }); + it('does not render dismiss button if it is set to non-dismissable', () => { + const wrapper = shallow( + {}} + ingestionMethod="crawler" + isDismissable={false} + isStartButtonDisabled={false} + /> + ); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.tsx new file mode 100644 index 0000000000000..8c0694cbb8405 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useActions } from 'kea'; + +import { + EuiButton, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiIcon, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { + TextExpansionCallOutState, + TextExpansionDismissButton, + FineTuneModelsButton, +} from './text_expansion_callout'; +import { TextExpansionCalloutLogic } from './text_expansion_callout_logic'; + +export const ModelDeployed = ({ + dismiss, + ingestionMethod, + isDismissable, + isStartButtonDisabled, +}: Pick< + TextExpansionCallOutState, + 'dismiss' | 'ingestionMethod' | 'isDismissable' | 'isStartButtonDisabled' +>) => { + const { startTextExpansionModel } = useActions(TextExpansionCalloutLogic); + + return ( + + + + + + + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedTitle', + { defaultMessage: 'Your ELSER model has deployed but not started.' } + )} +

+
+
+ {isDismissable && ( + + + + )} +
+
+ + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedBody', + { + defaultMessage: + 'You may start the model in a single-threaded configuration for testing, or tune the performance for a production environment.', + } + )} +

+
+
+ + + + + + + startTextExpansionModel(undefined)} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.startModelButton.label', + { + defaultMessage: 'Start single-threaded', + } + )} + + + + + + + +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.test.tsx new file mode 100644 index 0000000000000..f147778539f55 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.test.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { ModelDeploymentInProgress } from './model_deployment_in_progress'; +import { TextExpansionDismissButton } from './text_expansion_callout'; + +const DEFAULT_VALUES = { + startTextExpansionModelError: undefined, + isCreateButtonDisabled: false, + isModelDownloadInProgress: false, + isModelDownloaded: false, + isModelStarted: false, + isStartButtonDisabled: false, +}; + +describe('ModelDeploymentInProgress', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(DEFAULT_VALUES); + }); + it('renders dismiss button if it is set to dismissable', () => { + const wrapper = shallow( {}} isDismissable />); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); + }); + it('does not render dismiss button if it is set to non-dismissable', () => { + const wrapper = shallow( {}} isDismissable={false} />); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.tsx new file mode 100644 index 0000000000000..f9b9439833255 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.tsx @@ -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 React from 'react'; + +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiText, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { TextExpansionCallOutState, TextExpansionDismissButton } from './text_expansion_callout'; + +export const ModelDeploymentInProgress = ({ + dismiss, + isDismissable, +}: Pick) => ( + + + + + + + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingTitle', + { defaultMessage: 'Your ELSER model is deploying.' } + )} +

+
+
+ {isDismissable && ( + + + + )} +
+
+ + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingBody', + { + defaultMessage: + 'You can continue creating your pipeline with other uploaded models in the meantime.', + } + )} +

+
+
+
+
+); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.test.tsx new file mode 100644 index 0000000000000..c98ca42a41121 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.test.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiText } from '@elastic/eui'; + +import { ModelStarted } from './model_started'; +import { TextExpansionDismissButton, FineTuneModelsButton } from './text_expansion_callout'; + +const DEFAULT_VALUES = { + startTextExpansionModelError: undefined, + isCreateButtonDisabled: false, + isModelDownloadInProgress: false, + isModelDownloaded: false, + isModelStarted: false, + isStartButtonDisabled: false, +}; + +describe('ModelStarted', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(DEFAULT_VALUES); + }); + it('renders dismiss button if it is set to dismissable', () => { + const wrapper = shallow( + {}} isCompact={false} isDismissable isSingleThreaded /> + ); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); + }); + it('does not render dismiss button if it is set to non-dismissable', () => { + const wrapper = shallow( + {}} isCompact={false} isDismissable={false} isSingleThreaded /> + ); + expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); + }); + it('renders fine-tune button if the model is running single-threaded', () => { + const wrapper = shallow( + {}} isCompact={false} isDismissable isSingleThreaded /> + ); + expect(wrapper.find(FineTuneModelsButton).length).toBe(1); + }); + it('does not render description if it is set to compact', () => { + const wrapper = shallow( + {}} isCompact isDismissable isSingleThreaded /> + ); + expect(wrapper.find(EuiText).length).toBe(1); // Title only + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.tsx new file mode 100644 index 0000000000000..3c400854a5df0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.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 { + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, + EuiButtonEmpty, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { KibanaLogic } from '../../../../../shared/kibana'; + +import { + TextExpansionCallOutState, + TextExpansionDismissButton, + FineTuneModelsButton, +} from './text_expansion_callout'; +import { TRAINED_MODELS_PATH } from './utils'; + +export const ModelStarted = ({ + dismiss, + isCompact, + isDismissable, + isSingleThreaded, +}: Pick< + TextExpansionCallOutState, + 'dismiss' | 'isCompact' | 'isDismissable' | 'isSingleThreaded' +>) => ( + + + + + + + + + +

+ {isSingleThreaded + ? isCompact + ? i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitleCompact', + { defaultMessage: 'Your ELSER model is running single-threaded.' } + ) + : i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitle', + { defaultMessage: 'Your ELSER model has started single-threaded.' } + ) + : isCompact + ? i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitleCompact', + { defaultMessage: 'Your ELSER model is running.' } + ) + : i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitle', + { defaultMessage: 'Your ELSER model has started.' } + )} +

+
+
+ {isDismissable && ( + + + + )} +
+
+ {!isCompact && ( + <> + + +

+ {isSingleThreaded + ? i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedBody', + { + defaultMessage: + 'This single-threaded configuration is great for testing your custom inference pipelines, however performance should be fine-tuned for production.', + } + ) + : i18n.translate( + 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedBody', + { + defaultMessage: + 'Enjoy the power of ELSER in your custom Inference pipeline.', + } + )} +

+
+
+ + + + {isSingleThreaded ? ( + + ) : ( + + KibanaLogic.values.navigateToUrl(TRAINED_MODELS_PATH, { + shouldNotCreateHref: true, + }) + } + > + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.viewModelsButton', + { + defaultMessage: 'View details', + } + )} + + )} + + + + + )} +
+
+); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx index 2b7b28732fc91..a1ee2410128c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx @@ -11,20 +11,13 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiButton, EuiText } from '@elastic/eui'; - import { HttpError } from '../../../../../../../common/types/api'; -import { - TextExpansionCallOut, - DeployModel, - ModelDeploymentInProgress, - ModelDeployed, - TextExpansionDismissButton, - ModelStarted, - FineTuneModelsButton, -} from './text_expansion_callout'; - +import { DeployModel } from './deploy_model'; +import { ModelDeployed } from './model_deployed'; +import { ModelDeploymentInProgress } from './model_deployment_in_progress'; +import { ModelStarted } from './model_started'; +import { TextExpansionCallOut } from './text_expansion_callout'; import { TextExpansionErrors } from './text_expansion_errors'; jest.mock('./text_expansion_callout_data', () => ({ @@ -97,146 +90,4 @@ describe('TextExpansionCallOut', () => { const wrapper = shallow(); expect(wrapper.find(ModelStarted).length).toBe(1); }); - - describe('DeployModel', () => { - it('renders deploy button', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isCreateButtonDisabled={false} - isDismissable={false} - /> - ); - expect(wrapper.find(EuiButton).length).toBe(1); - const button = wrapper.find(EuiButton); - expect(button.prop('disabled')).toBe(false); - }); - it('renders disabled deploy button if it is set to disabled', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isCreateButtonDisabled - isDismissable={false} - /> - ); - expect(wrapper.find(EuiButton).length).toBe(1); - const button = wrapper.find(EuiButton); - expect(button.prop('disabled')).toBe(true); - }); - it('renders dismiss button if it is set to dismissable', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isCreateButtonDisabled={false} - isDismissable - /> - ); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); - }); - it('does not render dismiss button if it is set to non-dismissable', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isCreateButtonDisabled={false} - isDismissable={false} - /> - ); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); - }); - }); - - describe('ModelDeploymentInProgress', () => { - it('renders dismiss button if it is set to dismissable', () => { - const wrapper = shallow( {}} isDismissable />); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); - }); - it('does not render dismiss button if it is set to non-dismissable', () => { - const wrapper = shallow( - {}} isDismissable={false} /> - ); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); - }); - }); - - describe('ModelDeployed', () => { - it('renders start button', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isDismissable={false} - isStartButtonDisabled={false} - /> - ); - expect(wrapper.find(EuiButton).length).toBe(1); - const button = wrapper.find(EuiButton); - expect(button.prop('disabled')).toBe(false); - }); - it('renders disabled start button if it is set to disabled', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isDismissable={false} - isStartButtonDisabled - /> - ); - expect(wrapper.find(EuiButton).length).toBe(1); - const button = wrapper.find(EuiButton); - expect(button.prop('disabled')).toBe(true); - }); - it('renders dismiss button if it is set to dismissable', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isDismissable - isStartButtonDisabled={false} - /> - ); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); - }); - it('does not render dismiss button if it is set to non-dismissable', () => { - const wrapper = shallow( - {}} - ingestionMethod="crawler" - isDismissable={false} - isStartButtonDisabled={false} - /> - ); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); - }); - }); - - describe('ModelStarted', () => { - it('renders dismiss button if it is set to dismissable', () => { - const wrapper = shallow( - {}} isCompact={false} isDismissable isSingleThreaded /> - ); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(1); - }); - it('does not render dismiss button if it is set to non-dismissable', () => { - const wrapper = shallow( - {}} isCompact={false} isDismissable={false} isSingleThreaded /> - ); - expect(wrapper.find(TextExpansionDismissButton).length).toBe(0); - }); - it('renders fine-tune button if the model is running single-threaded', () => { - const wrapper = shallow( - {}} isCompact={false} isDismissable isSingleThreaded /> - ); - expect(wrapper.find(FineTuneModelsButton).length).toBe(1); - }); - it('does not render description if it is set to compact', () => { - const wrapper = shallow( - {}} isCompact isDismissable isSingleThreaded /> - ); - expect(wrapper.find(EuiText).length).toBe(1); // Title only - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx index 6743d79d820b9..7ded5c3e9035d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx @@ -7,32 +7,22 @@ import React from 'react'; -import { useActions, useValues } from 'kea'; +import { useValues } from 'kea'; -import { - EuiBadge, - EuiButton, - EuiButtonEmpty, - EuiButtonIcon, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiLink, - EuiSpacer, - EuiText, -} from '@elastic/eui'; +import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage, FormattedHTMLMessage } from '@kbn/i18n-react'; -import { docLinks } from '../../../../../shared/doc_links'; import { KibanaLogic } from '../../../../../shared/kibana'; - import { IndexViewLogic } from '../../index_view_logic'; +import { DeployModel } from './deploy_model'; +import { ModelDeployed } from './model_deployed'; +import { ModelDeploymentInProgress } from './model_deployment_in_progress'; +import { ModelStarted } from './model_started'; import { useTextExpansionCallOutData } from './text_expansion_callout_data'; import { getTextExpansionError, TextExpansionCalloutLogic } from './text_expansion_callout_logic'; import { TextExpansionErrors } from './text_expansion_errors'; +import { TRAINED_MODELS_PATH } from './utils'; export interface TextExpansionCallOutState { dismiss: () => void; @@ -50,8 +40,6 @@ export interface TextExpansionCallOutProps { isDismissable?: boolean; } -const TRAINED_MODELS_PATH = '/app/ml/trained_models'; - export const TextExpansionDismissButton = ({ dismiss, }: Pick) => { @@ -86,336 +74,6 @@ export const FineTuneModelsButton: React.FC = () => ( ); -export const DeployModel = ({ - dismiss, - ingestionMethod, - isCreateButtonDisabled, - isDismissable, -}: Pick< - TextExpansionCallOutState, - 'dismiss' | 'ingestionMethod' | 'isCreateButtonDisabled' | 'isDismissable' ->) => { - const { createTextExpansionModel } = useActions(TextExpansionCalloutLogic); - - return ( - - - - - - - - - - - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.title', - { defaultMessage: 'Improve your results with ELSER' } - )} -

-
-
- {isDismissable && ( - - - - )} -
-
- - - - - - - - - - - createTextExpansionModel(undefined)} - > - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.deployButton.label', - { - defaultMessage: 'Deploy', - } - )} - - - - - - - - - - - -
-
- ); -}; - -export const ModelDeploymentInProgress = ({ - dismiss, - isDismissable, -}: Pick) => ( - - - - - - - - - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingTitle', - { defaultMessage: 'Your ELSER model is deploying.' } - )} -

-
-
- {isDismissable && ( - - - - )} -
-
- - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingBody', - { - defaultMessage: - 'You can continue creating your pipeline with other uploaded models in the meantime.', - } - )} -

-
-
-
-
-); - -export const ModelDeployed = ({ - dismiss, - ingestionMethod, - isDismissable, - isStartButtonDisabled, -}: Pick< - TextExpansionCallOutState, - 'dismiss' | 'ingestionMethod' | 'isDismissable' | 'isStartButtonDisabled' ->) => { - const { startTextExpansionModel } = useActions(TextExpansionCalloutLogic); - - return ( - - - - - - - - - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedTitle', - { defaultMessage: 'Your ELSER model has deployed but not started.' } - )} -

-
-
- {isDismissable && ( - - - - )} -
-
- - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedBody', - { - defaultMessage: - 'You may start the model in a single-threaded configuration for testing, or tune the performance for a production environment.', - } - )} -

-
-
- - - - - - - startTextExpansionModel(undefined)} - > - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.startModelButton.label', - { - defaultMessage: 'Start single-threaded', - } - )} - - - - - - - -
-
- ); -}; - -export const ModelStarted = ({ - dismiss, - isCompact, - isDismissable, - isSingleThreaded, -}: Pick< - TextExpansionCallOutState, - 'dismiss' | 'isCompact' | 'isDismissable' | 'isSingleThreaded' ->) => ( - - - - - - - - - -

- {isSingleThreaded - ? isCompact - ? i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitleCompact', - { defaultMessage: 'Your ELSER model is running single-threaded.' } - ) - : i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitle', - { defaultMessage: 'Your ELSER model has started single-threaded.' } - ) - : isCompact - ? i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitleCompact', - { defaultMessage: 'Your ELSER model is running.' } - ) - : i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitle', - { defaultMessage: 'Your ELSER model has started.' } - )} -

-
-
- {isDismissable && ( - - - - )} -
-
- {!isCompact && ( - <> - - -

- {isSingleThreaded - ? i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedBody', - { - defaultMessage: - 'This single-threaded configuration is great for testing your custom inference pipelines, however performance should be fine-tuned for production.', - } - ) - : i18n.translate( - 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedBody', - { - defaultMessage: - 'Enjoy the power of ELSER in your custom Inference pipeline.', - } - )} -

-
-
- - - - {isSingleThreaded ? ( - - ) : ( - - KibanaLogic.values.navigateToUrl(TRAINED_MODELS_PATH, { - shouldNotCreateHref: true, - }) - } - > - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.viewModelsButton', - { - defaultMessage: 'View details', - } - )} - - )} - - - - - )} -
-
-); - export const TextExpansionCallOut: React.FC = (props) => { const { dismiss, isCompact, isDismissable, show } = useTextExpansionCallOutData(props); const { ingestionMethod } = useValues(IndexViewLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts index e70468d684279..d05b1453547bd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts @@ -12,6 +12,8 @@ import { FetchPipelineResponse } from '../../../../api/pipelines/fetch_pipeline' import { AddInferencePipelineFormErrors, InferencePipelineConfiguration } from './types'; const VALID_PIPELINE_NAME_REGEX = /^[\w\-]+$/; +export const TRAINED_MODELS_PATH = '/app/ml/trained_models'; + export const isValidPipelineName = (input: string): boolean => { return input.length > 0 && VALID_PIPELINE_NAME_REGEX.test(input); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/jest.config.js new file mode 100644 index 0000000000000..a55b8bbc715f4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/jest.config.js @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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/enterprise_search/public/applications/enterprise_search_content', + ], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/jest.config.js new file mode 100644 index 0000000000000..fd5a6db3b8e0c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/jest.config.js @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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/enterprise_search/public/applications/enterprise_search_overview', + ], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/esre/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/esre/jest.config.js new file mode 100644 index 0000000000000..e6ef9e507cf27 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/esre/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/enterprise_search/public/applications/esre'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/esre', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/search_experiences/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/search_experiences/jest.config.js new file mode 100644 index 0000000000000..1e39c00ae9893 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/search_experiences/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/enterprise_search/public/applications/search_experiences'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/search_experiences', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts b/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts index 5533e6e5d3d3c..0f41254093984 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts @@ -10,6 +10,7 @@ import confluence_cloud from '../../../assets/source_icons/confluence_cloud.svg' import custom from '../../../assets/source_icons/custom.svg'; import dropbox from '../../../assets/source_icons/dropbox.svg'; import github from '../../../assets/source_icons/github.svg'; +import gmail from '../../../assets/source_icons/gmail.svg'; import google_cloud_storage from '../../../assets/source_icons/google_cloud_storage.svg'; import google_drive from '../../../assets/source_icons/google_drive.svg'; import jira_cloud from '../../../assets/source_icons/jira_cloud.svg'; @@ -23,6 +24,7 @@ import amazon_s3 from '../../../assets/source_icons/s3.svg'; import servicenow from '../../../assets/source_icons/servicenow.svg'; import sharepoint from '../../../assets/source_icons/sharepoint.svg'; import sharepoint_online from '../../../assets/source_icons/sharepoint_online.svg'; +import slack from '../../../assets/source_icons/slack.svg'; export const CONNECTOR_ICONS = { amazon_s3, @@ -31,6 +33,7 @@ export const CONNECTOR_ICONS = { custom, dropbox, github, + gmail, google_cloud_storage, google_drive, jira_cloud, @@ -43,4 +46,5 @@ export const CONNECTOR_ICONS = { servicenow, sharepoint, sharepoint_online, + slack, }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/shared/jest.config.js new file mode 100644 index 0000000000000..5ee13cc30aeaf --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/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/enterprise_search/public/applications/shared'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/shared', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/cloud/instructions.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/cloud/instructions.tsx index 84c634cd8633d..d19e264b30df2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/cloud/instructions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/cloud/instructions.tsx @@ -9,13 +9,7 @@ import React from 'react'; -import { - EuiPageContent_Deprecated as EuiPageContent, - EuiSteps, - EuiText, - EuiLink, - EuiCallOut, -} from '@elastic/eui'; +import { EuiPageSection, EuiSteps, EuiText, EuiLink, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -27,7 +21,7 @@ interface Props { } export const CloudSetupInstructions: React.FC = ({ productName, cloudDeploymentLink }) => ( - + = ({ productName, cloudDepl }, ]} /> - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx index dc21bfd608840..6e0c75f1beb80 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/instructions.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { - EuiPageContent_Deprecated as EuiPageContent, + EuiPageSection, EuiText, EuiSteps, EuiCode, @@ -27,7 +27,7 @@ interface Props { } export const SetupInstructions: React.FC = ({ productName }) => ( - + = ({ productName }) => ( }, ]} /> - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx index 9b3a3e61f70ad..7d277e3977ce2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.tsx @@ -11,7 +11,7 @@ import { useValues } from 'kea'; import { EuiPage, - EuiPageSideBar_Deprecated as EuiPageSideBar, + EuiPageSidebar, EuiPageBody, EuiSpacer, EuiFlexGroup, @@ -35,8 +35,8 @@ import './setup_guide.scss'; interface Props { children: React.ReactNode; - productName: string; productEuiIcon: 'logoAppSearch' | 'logoWorkplaceSearch' | 'logoEnterpriseSearch'; + productName: string; } export const SetupGuideLayout: React.FC = ({ children, productName, productEuiIcon }) => { @@ -46,7 +46,7 @@ export const SetupGuideLayout: React.FC = ({ children, productName, produ return ( - + {SETUP_GUIDE_TITLE} @@ -64,7 +64,7 @@ export const SetupGuideLayout: React.FC = ({ children, productName, produ {children} - + {isCloudEnabled ? ( diff --git a/x-pack/plugins/enterprise_search/public/applications/vector_search/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/vector_search/jest.config.js new file mode 100644 index 0000000000000..24158650aa75f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/vector_search/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/enterprise_search/public/applications/vector_search'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/vector_search', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss index 3287cb21783cb..317c87c3516a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss @@ -18,6 +18,7 @@ } &__body { + padding-top:0; position: relative; width: 100%; height: 100%; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx index 5ea3783fc206f..1b87f8fdce4b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx @@ -11,12 +11,12 @@ import { useRouteMatch } from 'react-router-dom'; import { useValues } from 'kea'; import { - EuiPage, - EuiPageSideBar_Deprecated as EuiPageSideBar, + EuiPageSidebar, EuiPageBody, - EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiPageSection, EuiCallOut, EuiSpacer, + EuiPageTemplate, } from '@elastic/eui'; import { AccountHeader, AccountSettingsSidebar, PrivateSourcesSidebar } from '..'; @@ -47,13 +47,22 @@ export const PersonalDashboardLayout: React.FC = ({ <> {pageChrome && } - - + + {useRouteMatch(PRIVATE_SOURCES_PATH) && } {useRouteMatch(PERSONAL_SETTINGS_PATH) && } - + - + {readOnlyMode && ( <> = ({ )} + {isLoading ? : children} - + - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/workplace_search/jest.config.js new file mode 100644 index 0000000000000..6ae1b5b9b1a84 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/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/enterprise_search/public/applications/workplace_search'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/workplace_search', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/jest.config.js b/x-pack/plugins/enterprise_search/public/jest.config.js index c527b85707b42..fec5a831f2fee 100644 --- a/x-pack/plugins/enterprise_search/public/jest.config.js +++ b/x-pack/plugins/enterprise_search/public/jest.config.js @@ -8,11 +8,15 @@ module.exports = { preset: '@kbn/test', rootDir: '../../../..', + /** all nested directories have their own Jest config file */ + testMatch: [ + '/x-pack/plugins/enterprise_search/public/applications/*.test.{js,mjs,ts,tsx}', + ], roots: ['/x-pack/plugins/enterprise_search/public'], collectCoverage: true, coverageReporters: ['text', 'html'], collectCoverageFrom: [ - '/x-pack/plugins/enterprise_search/**/*.{ts,tsx}', + '/x-pack/plugins/enterprise_search/public/applications/*.{ts,tsx}', '!/x-pack/plugins/enterprise_search/public/*.ts', '!/x-pack/plugins/enterprise_search/server/*.ts', '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts index 8e60b6b73ff40..6d2e67d602548 100644 --- a/x-pack/plugins/enterprise_search/server/integrations.ts +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -34,19 +34,6 @@ const workplaceSearchIntegrations: WorkplaceSearchIntegration[] = [ ), categories: ['enterprise_search', 'workplace_search_content_source'], }, - { - id: 'gmail', - title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.gmailName', { - defaultMessage: 'Gmail', - }), - description: i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.integrations.gmailDescription', - { - defaultMessage: 'Search over your emails managed by Gmail with Workplace Search.', - } - ), - categories: ['enterprise_search', 'google_cloud', 'workplace_search_content_source'], - }, { id: 'onedrive', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.onedriveName', { @@ -77,19 +64,6 @@ const workplaceSearchIntegrations: WorkplaceSearchIntegration[] = [ ), categories: ['enterprise_search', 'workplace_search_content_source'], }, - { - id: 'slack', - title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.slackName', { - defaultMessage: 'Slack', - }), - description: i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.integrations.slackDescription', - { - defaultMessage: 'Search over your messages on Slack with Workplace Search.', - } - ), - categories: ['enterprise_search', 'workplace_search_content_source'], - }, { id: 'zendesk', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.zendeskName', { @@ -303,6 +277,27 @@ export const registerEnterpriseSearchIntegrations = ( isBeta: false, }); + customIntegrations.registerCustomIntegration({ + id: 'gmail', + title: i18n.translate('xpack.enterpriseSearch.content.integrations.gmail', { + defaultMessage: 'Gmail', + }), + description: i18n.translate('xpack.enterpriseSearch.content.integrations.gmailDescription', { + defaultMessage: 'Search over your content on Gmail.', + }), + categories: ['enterprise_search', 'elastic_stack', 'connector', 'connector_client'], + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=gmail', + icons: [ + { + type: 'svg', + src: http.basePath.prepend('/plugins/enterpriseSearch/assets/source_icons/gmail.svg'), + }, + ], + shipper: 'enterprise_search', + isBeta: false, + }); + customIntegrations.registerCustomIntegration({ id: 'mongodb', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.mongoDBName', { @@ -547,6 +542,27 @@ export const registerEnterpriseSearchIntegrations = ( isBeta: false, }); + customIntegrations.registerCustomIntegration({ + id: 'slack', + title: i18n.translate('xpack.enterpriseSearch.content.integrations.slack', { + defaultMessage: 'Slack', + }), + description: i18n.translate('xpack.enterpriseSearch.content.integrations.slackDescription', { + defaultMessage: 'Search over your content on Slack.', + }), + categories: ['enterprise_search', 'elastic_stack', 'connector', 'connector_client'], + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=slack', + icons: [ + { + type: 'svg', + src: http.basePath.prepend('/plugins/enterpriseSearch/assets/source_icons/slack.svg'), + }, + ], + shipper: 'enterprise_search', + isBeta: false, + }); + customIntegrations.registerCustomIntegration({ id: 'oracle', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.oracleName', { diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts index 6659f0b19ebeb..2a5582347db74 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts @@ -11,8 +11,6 @@ const createClusterClientMock = () => { const mock: jest.Mocked = { indexDocument: jest.fn(), indexDocuments: jest.fn(), - doesIlmPolicyExist: jest.fn(), - createIlmPolicy: jest.fn(), doesIndexTemplateExist: jest.fn(), createIndexTemplate: jest.fn(), doesDataStreamExist: jest.fn(), diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts index 151a65573125c..6f36af2be1f78 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -165,56 +165,6 @@ describe('buffering documents', () => { }); }); -describe('doesIlmPolicyExist', () => { - // ElasticsearchError can be a bit random in shape, we need an any here - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const notFoundError = new Error('Not found') as any; - notFoundError.statusCode = 404; - - test('should call cluster with proper arguments', async () => { - await clusterClientAdapter.doesIlmPolicyExist('foo'); - expect(clusterClient.transport.request).toHaveBeenCalledWith({ - method: 'GET', - path: '/_ilm/policy/foo', - }); - }); - - test('should return false when 404 error is returned by Elasticsearch', async () => { - clusterClient.transport.request.mockRejectedValue(notFoundError); - await expect(clusterClientAdapter.doesIlmPolicyExist('foo')).resolves.toEqual(false); - }); - - test('should throw error when error is not 404', async () => { - clusterClient.transport.request.mockRejectedValue(new Error('Fail')); - await expect( - clusterClientAdapter.doesIlmPolicyExist('foo') - ).rejects.toThrowErrorMatchingInlineSnapshot(`"error checking existance of ilm policy: Fail"`); - }); - - test('should return true when no error is thrown', async () => { - await expect(clusterClientAdapter.doesIlmPolicyExist('foo')).resolves.toEqual(true); - }); -}); - -describe('createIlmPolicy', () => { - test('should call cluster client with given policy', async () => { - clusterClient.transport.request.mockResolvedValue({ success: true }); - await clusterClientAdapter.createIlmPolicy('foo', { args: true }); - expect(clusterClient.transport.request).toHaveBeenCalledWith({ - method: 'PUT', - path: '/_ilm/policy/foo', - body: { args: true }, - }); - }); - - test('should throw error when call cluster client throws', async () => { - clusterClient.transport.request.mockRejectedValue(new Error('Fail')); - await expect( - clusterClientAdapter.createIlmPolicy('foo', { args: true }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"error creating ilm policy: Fail"`); - }); -}); - describe('doesIndexTemplateExist', () => { test('should call cluster with proper arguments', async () => { await clusterClientAdapter.doesIndexTemplateExist('foo'); diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts index 8807e34cfedf3..27a86b839d6c9 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -178,36 +178,6 @@ export class ClusterClientAdapter { - const request = { - method: 'GET', - path: `/_ilm/policy/${policyName}`, - }; - try { - const esClient = await this.elasticsearchClientPromise; - await esClient.transport.request(request); - } catch (err) { - if (err.statusCode === 404) return false; - throw new Error(`error checking existance of ilm policy: ${err.message}`); - } - return true; - } - - public async createIlmPolicy(policyName: string, policy: Record): Promise { - this.logger.info(`Installing ILM policy ${policyName}`); - const request = { - method: 'PUT', - path: `/_ilm/policy/${policyName}`, - body: policy, - }; - try { - const esClient = await this.elasticsearchClientPromise; - await esClient.transport.request(request); - } catch (err) { - throw new Error(`error creating ilm policy: ${err.message}`); - } - } - public async doesIndexTemplateExist(name: string): Promise { try { const esClient = await this.elasticsearchClientPromise; diff --git a/x-pack/plugins/event_log/server/es/context.test.ts b/x-pack/plugins/event_log/server/es/context.test.ts index 67ea2a95151f2..681b927478d81 100644 --- a/x-pack/plugins/event_log/server/es/context.test.ts +++ b/x-pack/plugins/event_log/server/es/context.test.ts @@ -57,13 +57,12 @@ describe('createEsContext', () => { expect(esNames).toStrictEqual({ base: 'test-index', dataStream: 'test-index-event-log-1.2.3', - ilmPolicy: 'test-index-event-log-policy', indexPattern: 'test-index-event-log-*', indexTemplate: 'test-index-event-log-1.2.3-template', }); }); - test('should return exist false for esAdapter ilm policy, index template and data stream before initialize', async () => { + test('should return exist false for esAdapter index template and data stream before initialize', async () => { const context = createEsContext({ logger, indexNameRoot: 'test1', @@ -84,7 +83,7 @@ describe('createEsContext', () => { expect(doesIndexTemplateExist).toBeFalsy(); }); - test('should return exist true for esAdapter ilm policy, index template and data stream after initialize', async () => { + test('should return exist true for esAdapter index template and data stream after initialize', async () => { const context = createEsContext({ logger, indexNameRoot: 'test2', @@ -94,11 +93,6 @@ describe('createEsContext', () => { elasticsearchClient.indices.existsTemplate.mockResponse(true); context.initialize(); - const doesIlmPolicyExist = await context.esAdapter.doesIlmPolicyExist( - context.esNames.ilmPolicy - ); - expect(doesIlmPolicyExist).toBeTruthy(); - elasticsearchClient.indices.getDataStream.mockResolvedValue(GetDataStreamsResponse); const doesDataStreamExist = await context.esAdapter.doesDataStreamExist( context.esNames.dataStream diff --git a/x-pack/plugins/event_log/server/es/documents.test.ts b/x-pack/plugins/event_log/server/es/documents.test.ts index 814596f751c61..71b75ee3ca3dc 100644 --- a/x-pack/plugins/event_log/server/es/documents.test.ts +++ b/x-pack/plugins/event_log/server/es/documents.test.ts @@ -5,19 +5,9 @@ * 2.0. */ -import { getIndexTemplate, getIlmPolicy } from './documents'; +import { getIndexTemplate } from './documents'; import { getEsNames } from './names'; -describe('getIlmPolicy()', () => { - test('returns the basic structure of an ilm policy', () => { - expect(getIlmPolicy()).toMatchObject({ - policy: { - phases: {}, - }, - }); - }); -}); - describe('getIndexTemplate()', () => { const kibanaVersion = '1.2.3'; const esNames = getEsNames('XYZ', kibanaVersion); @@ -27,7 +17,6 @@ describe('getIndexTemplate()', () => { expect(indexTemplate.index_patterns).toEqual([esNames.dataStream]); expect(indexTemplate.template.settings.number_of_shards).toBeGreaterThanOrEqual(0); expect(indexTemplate.template.settings.auto_expand_replicas).toBe('0-1'); - expect(indexTemplate.template.settings['index.lifecycle.name']).toBe(esNames.ilmPolicy); expect(indexTemplate.template.mappings).toMatchObject({}); }); }); diff --git a/x-pack/plugins/event_log/server/es/documents.ts b/x-pack/plugins/event_log/server/es/documents.ts index deaa8349971f9..0f654f80ad55b 100644 --- a/x-pack/plugins/event_log/server/es/documents.ts +++ b/x-pack/plugins/event_log/server/es/documents.ts @@ -25,7 +25,9 @@ export function getIndexTemplate(esNames: EsNames) { hidden: true, number_of_shards: 1, auto_expand_replicas: '0-1', - 'index.lifecycle.name': esNames.ilmPolicy, + }, + lifecycle: { + data_retention: '90d', }, mappings, }, @@ -33,33 +35,3 @@ export function getIndexTemplate(esNames: EsNames) { return indexTemplateBody; } - -// returns the body of an ilm policy used in an ES PUT _ilm/policy call -export function getIlmPolicy() { - return { - policy: { - _meta: { - description: - 'ilm policy the Kibana event log, created initially by Kibana, but updated by the user, not Kibana', - managed: false, - }, - phases: { - hot: { - actions: { - rollover: { - max_size: '50GB', - max_age: '30d', - // max_docs: 1, // you know, for testing - }, - }, - }, - delete: { - min_age: '90d', - actions: { - delete: {}, - }, - }, - }, - }, - }; -} diff --git a/x-pack/plugins/event_log/server/es/init.test.ts b/x-pack/plugins/event_log/server/es/init.test.ts index 30e220313b26b..7c81ae80b8823 100644 --- a/x-pack/plugins/event_log/server/es/init.test.ts +++ b/x-pack/plugins/event_log/server/es/init.test.ts @@ -83,7 +83,6 @@ describe('initializeEs', () => { `error getting existing index templates - Fail` ); expect(esContext.esAdapter.setLegacyIndexTemplateToHidden).not.toHaveBeenCalled(); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); }); test(`should continue initialization if updating existing index templates throws an error`, async () => { @@ -124,7 +123,6 @@ describe('initializeEs', () => { expect(esContext.logger.error).toHaveBeenCalledWith( `error setting existing \"foo-bar-template\" index template to hidden - Fail` ); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); }); test(`should update existing index settings if any exist and are not hidden`, async () => { @@ -207,7 +205,6 @@ describe('initializeEs', () => { expect(esContext.esAdapter.getExistingIndices).toHaveBeenCalled(); expect(esContext.logger.error).toHaveBeenCalledWith(`error getting existing indices - Fail`); expect(esContext.esAdapter.setIndexToHidden).not.toHaveBeenCalled(); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); }); test(`should continue initialization if updating existing index settings throws an error`, async () => { @@ -251,7 +248,6 @@ describe('initializeEs', () => { expect(esContext.logger.error).toHaveBeenCalledWith( `error setting existing \"foo-bar-000001\" index to hidden - Fail` ); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); }); test(`should update existing index aliases if any exist and are not hidden`, async () => { @@ -300,7 +296,6 @@ describe('initializeEs', () => { `error getting existing index aliases - Fail` ); expect(esContext.esAdapter.setIndexAliasToHidden).not.toHaveBeenCalled(); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); }); test(`should continue initialization if updating existing index aliases throws an error`, async () => { @@ -336,23 +331,6 @@ describe('initializeEs', () => { expect(esContext.logger.error).toHaveBeenCalledWith( `error setting existing \"foo-bar\" index aliases - Fail` ); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); - }); - - test(`should create ILM policy if it doesn't exist`, async () => { - esContext.esAdapter.doesIlmPolicyExist.mockResolvedValue(false); - - await initializeEs(esContext); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); - expect(esContext.esAdapter.createIlmPolicy).toHaveBeenCalled(); - }); - - test(`shouldn't create ILM policy if it exists`, async () => { - esContext.esAdapter.doesIlmPolicyExist.mockResolvedValue(true); - - await initializeEs(esContext); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); - expect(esContext.esAdapter.createIlmPolicy).not.toHaveBeenCalled(); }); test(`should create index template if it doesn't exist`, async () => { @@ -463,30 +441,10 @@ describe('retries', () => { esContext.esAdapter.getExistingLegacyIndexTemplates.mockResolvedValue({}); esContext.esAdapter.getExistingIndices.mockResolvedValue({}); esContext.esAdapter.getExistingIndexAliases.mockResolvedValue({}); - esContext.esAdapter.doesIlmPolicyExist.mockResolvedValue(true); esContext.esAdapter.doesIndexTemplateExist.mockResolvedValue(true); esContext.esAdapter.doesDataStreamExist.mockResolvedValue(true); }); - test('createIlmPolicyIfNotExists with 1 retry', async () => { - esContext.esAdapter.doesIlmPolicyExist.mockRejectedValueOnce(new Error('retry 1')); - - const timeStart = performance.now(); - await initializeEs(esContext); - const timeElapsed = Math.ceil(performance.now() - timeStart); - - expect(timeElapsed).toBeGreaterThanOrEqual(MOCK_RETRY_DELAY); - - expect(esContext.esAdapter.getExistingLegacyIndexTemplates).toHaveBeenCalledTimes(1); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalledTimes(2); - expect(esContext.esAdapter.doesIndexTemplateExist).toHaveBeenCalledTimes(1); - expect(esContext.esAdapter.doesDataStreamExist).toHaveBeenCalledTimes(1); - - const prefix = `eventLog initialization operation failed and will be retried: createIlmPolicyIfNotExists`; - expect(esContext.logger.warn).toHaveBeenCalledTimes(1); - expect(esContext.logger.warn).toHaveBeenCalledWith(`${prefix}; 4 more times; error: retry 1`); - }); - test('createIndexTemplateIfNotExists with 2 retries', async () => { esContext.esAdapter.doesIndexTemplateExist.mockRejectedValueOnce(new Error('retry 2a')); esContext.esAdapter.doesIndexTemplateExist.mockRejectedValueOnce(new Error('retry 2b')); @@ -498,7 +456,6 @@ describe('retries', () => { expect(timeElapsed).toBeGreaterThanOrEqual(MOCK_RETRY_DELAY * (1 + 2)); expect(esContext.esAdapter.getExistingLegacyIndexTemplates).toHaveBeenCalledTimes(1); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalledTimes(1); expect(esContext.esAdapter.doesIndexTemplateExist).toHaveBeenCalledTimes(3); expect(esContext.esAdapter.doesDataStreamExist).toHaveBeenCalledTimes(1); @@ -524,7 +481,6 @@ describe('retries', () => { expect(timeElapsed).toBeGreaterThanOrEqual(MOCK_RETRY_DELAY * (1 + 2 + 4 + 8)); expect(esContext.esAdapter.getExistingLegacyIndexTemplates).toHaveBeenCalledTimes(1); - expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalledTimes(1); expect(esContext.esAdapter.doesIndexTemplateExist).toHaveBeenCalledTimes(1); expect(esContext.esAdapter.doesDataStreamExist).toHaveBeenCalledTimes(5); expect(esContext.esAdapter.createDataStream).toHaveBeenCalledTimes(0); diff --git a/x-pack/plugins/event_log/server/es/init.ts b/x-pack/plugins/event_log/server/es/init.ts index 6eb4d5736a4a1..cf737cbf035c6 100644 --- a/x-pack/plugins/event_log/server/es/init.ts +++ b/x-pack/plugins/event_log/server/es/init.ts @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { asyncForEach } from '@kbn/std'; import { groupBy } from 'lodash'; import pRetry, { FailedAttemptError } from 'p-retry'; -import { getIlmPolicy, getIndexTemplate } from './documents'; +import { getIndexTemplate } from './documents'; import { EsContext } from './context'; const MAX_RETRY_DELAY = 30000; @@ -33,7 +33,6 @@ async function initializeEsResources(esContext: EsContext) { // today, setExistingAssetsToHidden() never throws, but just in case ... await retry(steps.setExistingAssetsToHidden); - await retry(steps.createIlmPolicyIfNotExists); await retry(steps.createIndexTemplateIfNotExists); await retry(steps.createDataStreamIfNotExists); @@ -202,18 +201,6 @@ class EsInitializationSteps { await this.setExistingIndexAliasesToHidden(); } - async createIlmPolicyIfNotExists(): Promise { - const exists = await this.esContext.esAdapter.doesIlmPolicyExist( - this.esContext.esNames.ilmPolicy - ); - if (!exists) { - await this.esContext.esAdapter.createIlmPolicy( - this.esContext.esNames.ilmPolicy, - getIlmPolicy() - ); - } - } - async createIndexTemplateIfNotExists(): Promise { const exists = await this.esContext.esAdapter.doesIndexTemplateExist( this.esContext.esNames.indexTemplate diff --git a/x-pack/plugins/event_log/server/es/names.mock.ts b/x-pack/plugins/event_log/server/es/names.mock.ts index 138d99aa706ea..837abe9dd413b 100644 --- a/x-pack/plugins/event_log/server/es/names.mock.ts +++ b/x-pack/plugins/event_log/server/es/names.mock.ts @@ -11,7 +11,6 @@ const createNamesMock = () => { const mock: jest.Mocked = { base: '.kibana', dataStream: '.kibana-event-log-8.0.0', - ilmPolicy: 'kibana-event-log-policy', indexPattern: '.kibana-event-log-*', indexTemplate: '.kibana-event-log-8.0.0-template', }; diff --git a/x-pack/plugins/event_log/server/es/names.test.ts b/x-pack/plugins/event_log/server/es/names.test.ts index 0a05d560b9c94..63d1ad9d398a7 100644 --- a/x-pack/plugins/event_log/server/es/names.test.ts +++ b/x-pack/plugins/event_log/server/es/names.test.ts @@ -18,16 +18,7 @@ describe('getEsNames()', () => { const esNames = getEsNames(base, kibanaVersion); expect(esNames.base).toEqual(base); expect(esNames.dataStream).toEqual(`${base}-event-log-${kibanaVersion}`); - expect(esNames.ilmPolicy).toEqual(`${base}-event-log-policy`); expect(esNames.indexPattern).toEqual(`${base}-event-log-*`); expect(esNames.indexTemplate).toEqual(`${base}-event-log-${kibanaVersion}-template`); }); - - test('ilm policy name does not contain dot prefix', () => { - const base = '.XYZ'; - const kibanaVersion = '1.2.3'; - - const esNames = getEsNames(base, kibanaVersion); - expect(esNames.ilmPolicy).toEqual('XYZ-event-log-policy'); - }); }); diff --git a/x-pack/plugins/event_log/server/es/names.ts b/x-pack/plugins/event_log/server/es/names.ts index d807e53c6abbb..0e48ca911b95a 100644 --- a/x-pack/plugins/event_log/server/es/names.ts +++ b/x-pack/plugins/event_log/server/es/names.ts @@ -10,7 +10,6 @@ const EVENT_LOG_NAME_SUFFIX = `-event-log`; export interface EsNames { base: string; dataStream: string; - ilmPolicy: string; indexPattern: string; indexTemplate: string; } @@ -19,13 +18,9 @@ export function getEsNames(baseName: string, kibanaVersion: string): EsNames { const EVENT_LOG_VERSION_SUFFIX = `-${kibanaVersion.toLocaleLowerCase()}`; const eventLogName = `${baseName}${EVENT_LOG_NAME_SUFFIX}`; const eventLogNameWithVersion = `${eventLogName}${EVENT_LOG_VERSION_SUFFIX}`; - const eventLogPolicyName = `${ - baseName.startsWith('.') ? baseName.substring(1) : baseName - }${EVENT_LOG_NAME_SUFFIX}-policy`; return { base: baseName, dataStream: eventLogNameWithVersion, - ilmPolicy: `${eventLogPolicyName}`, indexPattern: `${eventLogName}-*`, indexTemplate: `${eventLogNameWithVersion}-template`, }; diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx index 71f7cfec8e5d2..a4d8a88507e82 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx @@ -110,6 +110,7 @@ describe('AddToCaseAction', function () { update: false, delete: false, push: false, + connectors: false, }, }) ); diff --git a/x-pack/plugins/fleet/common/constants/output.ts b/x-pack/plugins/fleet/common/constants/output.ts index b23fbc3cc2c2a..59f3ceff36a0f 100644 --- a/x-pack/plugins/fleet/common/constants/output.ts +++ b/x-pack/plugins/fleet/common/constants/output.ts @@ -42,6 +42,12 @@ export const kafkaAuthType = { Userpass: 'user_pass', Ssl: 'ssl', Kerberos: 'kerberos', + None: 'none', +} as const; + +export const kafkaConnectionType = { + Plaintext: 'plaintext', + Encryption: 'encryption', } as const; export const kafkaSaslMechanism = { @@ -60,18 +66,19 @@ export const kafkaTopicWhenType = { Equals: 'equals', Contains: 'contains', Regexp: 'regexp', - Range: 'range', - Network: 'network', - HasFields: 'has_fields', - Or: 'or', - And: 'and', - Not: 'not', } as const; export const kafkaAcknowledgeReliabilityLevel = { - Commit: 'Wait for local commit', - Replica: 'Wait for all replicas to commit', - DoNotWait: 'Do not wait', + Commit: 1, + Replica: -1, + DoNotWait: 0, +} as const; + +export const kafkaVerificationModes = { + Full: 'full', + None: 'none', + Strict: 'strict', + Certificate: 'certificate', } as const; export const kafkaSupportedVersions = [ diff --git a/x-pack/plugins/fleet/common/constants/secrets.ts b/x-pack/plugins/fleet/common/constants/secrets.ts index 4c42b1d68013e..06d370ff81323 100644 --- a/x-pack/plugins/fleet/common/constants/secrets.ts +++ b/x-pack/plugins/fleet/common/constants/secrets.ts @@ -5,4 +5,4 @@ * 2.0. */ -export const SECRETS_INDEX = '.fleet-secrets'; +export const SECRETS_ENDPOINT_PATH = '/_fleet/secret'; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index cc1ad5c98c32a..200cebba48cc7 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -7701,6 +7701,15 @@ }, "key": { "type": "string" + }, + "verification_mode": { + "type": "string", + "enum": [ + "none", + "full", + "certificate", + "strict" + ] } } }, @@ -7751,6 +7760,13 @@ "auth_type": { "type": "string" }, + "connection_type": { + "type": "string", + "enum": [ + "plaintext", + "encryption" + ] + }, "username": { "type": "string" }, @@ -7826,11 +7842,8 @@ "broker_timeout": { "type": "number" }, - "broker_buffer_size": { + "required_acks": { "type": "number" - }, - "broker_ack_reliability": { - "type": "string" } }, "required": [ @@ -8103,6 +8116,15 @@ }, "key": { "type": "string" + }, + "verification_mode": { + "type": "string", + "enum": [ + "none", + "full", + "certificate", + "strict" + ] } } }, @@ -8153,6 +8175,13 @@ "auth_type": { "type": "string" }, + "connection_type": { + "type": "string", + "enum": [ + "plaintext", + "encryption" + ] + }, "username": { "type": "string" }, @@ -8228,10 +8257,7 @@ "broker_timeout": { "type": "number" }, - "broker_ack_reliability": { - "type": "string" - }, - "broker_buffer_size": { + "required_acks": { "type": "number" } }, diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 0d72245fa2d99..6c216e464a104 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -4959,6 +4959,13 @@ components: type: string key: type: string + verification_mode: + type: string + enum: + - none + - full + - certificate + - strict proxy_id: type: string shipper: @@ -4990,6 +4997,11 @@ components: type: string auth_type: type: string + connection_type: + type: string + enum: + - plaintext + - encryption username: type: string password: @@ -5038,10 +5050,8 @@ components: type: number broker_timeout: type: number - broker_buffer_size: + required_acks: type: number - broker_ack_reliability: - type: string required: - name - type @@ -5223,6 +5233,13 @@ components: type: string key: type: string + verification_mode: + type: string + enum: + - none + - full + - certificate + - strict proxy_id: type: string shipper: @@ -5254,6 +5271,11 @@ components: type: string auth_type: type: string + connection_type: + type: string + enum: + - plaintext + - encryption username: type: string password: @@ -5302,9 +5324,7 @@ components: type: number broker_timeout: type: number - broker_ack_reliability: - type: string - broker_buffer_size: + required_acks: type: number required: - name diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_kafka.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_kafka.yaml index dbed2b44dc08a..fa76c2301ed94 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_kafka.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_kafka.yaml @@ -35,6 +35,9 @@ properties: type: string key: type: string + verification_mode: + type: string + enum: ['none', 'full', 'certificate', 'strict'] proxy_id: type: string shipper: @@ -66,6 +69,9 @@ properties: type: string auth_type: type: string + connection_type: + type: string + enum: ['plaintext', 'encryption'] username: type: string password: @@ -114,10 +120,8 @@ properties: type: number broker_timeout: type: number - broker_buffer_size: + required_acks: type: number - broker_ack_reliability: - type: string required: - name - type diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_kafka.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_kafka.yaml index bb1e76fa70a55..2ce5525a1a9f4 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_kafka.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_kafka.yaml @@ -35,6 +35,9 @@ properties: type: string key: type: string + verification_mode: + type: string + enum: ['none', 'full', 'certificate', 'strict'] proxy_id: type: string shipper: @@ -66,6 +69,9 @@ properties: type: string auth_type: type: string + connection_type: + type: string + enum: ['plaintext', 'encryption'] username: type: string password: @@ -114,9 +120,7 @@ properties: type: number broker_timeout: type: number - broker_ack_reliability: - type: string - broker_buffer_size: + required_acks: type: number required: - name diff --git a/x-pack/plugins/fleet/common/types/models/output.ts b/x-pack/plugins/fleet/common/types/models/output.ts index 95f353575afd6..a537e0ab0233b 100644 --- a/x-pack/plugins/fleet/common/types/models/output.ts +++ b/x-pack/plugins/fleet/common/types/models/output.ts @@ -11,14 +11,18 @@ import type { kafkaAuthType, kafkaCompressionType, kafkaSaslMechanism } from '.. import type { kafkaPartitionType } from '../../constants'; import type { kafkaTopicWhenType } from '../../constants'; import type { kafkaAcknowledgeReliabilityLevel } from '../../constants'; +import type { kafkaVerificationModes } from '../../constants'; +import type { kafkaConnectionType } from '../../constants'; export type OutputType = typeof outputType; export type KafkaCompressionType = typeof kafkaCompressionType; export type KafkaAuthType = typeof kafkaAuthType; +export type KafkaConnectionTypeType = typeof kafkaConnectionType; export type KafkaSaslMechanism = typeof kafkaSaslMechanism; export type KafkaPartitionType = typeof kafkaPartitionType; export type KafkaTopicWhenType = typeof kafkaTopicWhenType; export type KafkaAcknowledgeReliabilityLevel = typeof kafkaAcknowledgeReliabilityLevel; +export type KafkaVerificationMode = typeof kafkaVerificationModes; interface NewBaseOutput { is_default: boolean; @@ -34,6 +38,7 @@ interface NewBaseOutput { certificate_authorities?: string[]; certificate?: string; key?: string; + verification_mode?: ValueOf; } | null; proxy_id?: string | null; shipper?: ShipperOutput | null; @@ -76,6 +81,7 @@ export interface KafkaOutput extends NewBaseOutput { compression?: ValueOf; compression_level?: number; auth_type?: ValueOf; + connection_type?: ValueOf; username?: string; password?: string; sasl?: { @@ -105,6 +111,5 @@ export interface KafkaOutput extends NewBaseOutput { }>; timeout?: number; broker_timeout?: number; - broker_ack_reliability?: ValueOf; - broker_buffer_size?: number; + required_acks?: ValueOf; } diff --git a/x-pack/plugins/fleet/common/types/models/secret.ts b/x-pack/plugins/fleet/common/types/models/secret.ts index dea38da64cce8..cf95d88b82e2b 100644 --- a/x-pack/plugins/fleet/common/types/models/secret.ts +++ b/x-pack/plugins/fleet/common/types/models/secret.ts @@ -4,10 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { PackagePolicyConfigRecordEntry } from '../..'; export interface Secret { id: string; - value: string; } export interface SecretElasticDoc { @@ -18,7 +17,19 @@ export interface VarSecretReference { id: string; isSecretRef: true; } +export interface SecretPath { + path: string; + value: PackagePolicyConfigRecordEntry; +} // this is used in the top level secret_refs array on package and agent policies export interface PolicySecretReference { id: string; } + +export interface DeletedSecretResponse { + deleted: boolean; +} +export interface DeletedSecretReference { + id: string; + deleted: boolean; +} diff --git a/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts b/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts index 45ff5257fc7c2..2d9f33d8e1efa 100644 --- a/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts @@ -5,10 +5,6 @@ * 2.0. */ -import { allowedExperimentalValues } from '../../common/experimental_features'; - -import { ExperimentalFeaturesService } from '../../public/services'; - import { getSpecificSelectorId, SETTINGS_CONFIRM_MODAL_BTN, @@ -42,323 +38,358 @@ describe('Outputs', () => { }); describe('Kafka', () => { - ExperimentalFeaturesService.init(allowedExperimentalValues); - - const { kafkaOutput: isKafkaOutputEnabled } = ExperimentalFeaturesService.get(); - - // TODO: Remove IF statement once Kafka is GA - if (!isKafkaOutputEnabled) { - it('is not available', () => { - visit('/app/fleet/settings'); - }); - } else { - describe('Form validation', () => { - it('renders all form fields', () => { - selectKafkaOutput(); - - cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.VERSION_SELECT); - cy.get('[placeholder="Specify host"'); - cy.getBySel(SETTINGS_OUTPUTS.ADD_HOST_ROW_BTN); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SELECT).within(() => { - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_PASSWORD_OPTION); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SSL_OPTION); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_KERBEROS_OPTION); - }); - - // Verify user/pass fields - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT); - - // Verify SSL fields - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SSL_OPTION).click(); - cy.get('[placeholder="Specify certificate authority"]'); - cy.get('[placeholder="Specify ssl certificate"]'); - cy.get('[placeholder="Specify certificate key"]'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_PASSWORD_OPTION).click(); - - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SELECT).within(() => { - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_PLAIN_OPTION); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SCRAM_256_OPTION); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SCRAM_512_OPTION); - }); + describe('Form validation', () => { + it('renders all form fields', () => { + selectKafkaOutput(); + + cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.VERSION_SELECT); + cy.get('[placeholder="Specify host"'); + cy.getBySel(SETTINGS_OUTPUTS.ADD_HOST_ROW_BTN); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SELECT).within(() => { + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_NONE_OPTION); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_PASSWORD_OPTION); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SSL_OPTION); + }); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_PANEL).within(() => { - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_SELECT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_RANDOM_OPTION); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_OPTION); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_ROUND_ROBIN_OPTION); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_EVENTS_INPUT); - }); + // Verify user/pass fields + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT); + cy.get('[placeholder="Specify certificate authority"]'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_VERIFICATION_MODE_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_CONNECTION_TYPE_SELECT).should( + 'not.exist' + ); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SELECT).within(() => { + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_PLAIN_OPTION); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SCRAM_256_OPTION); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SCRAM_512_OPTION); + }); - // Verify Round Robin fields - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_RANDOM_OPTION).click(); + // Verify SSL fields + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SSL_OPTION).click(); + cy.get('[placeholder="Specify certificate authority"]'); + cy.get('[placeholder="Specify ssl certificate"]'); + cy.get('[placeholder="Specify certificate key"]'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_VERIFICATION_MODE_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_CONNECTION_TYPE_SELECT).should( + 'not.exist' + ); + + // Verify None fields + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_NONE_OPTION).click(); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SELECT).should('not.exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_INPUT).should('not.exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT).should('not.exist'); + cy.get('[placeholder="Specify ssl certificate"]').should('not.exist'); + cy.get('[placeholder="Specify certificate key"]').should('not.exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_VERIFICATION_MODE_INPUT).should( + 'not.exist' + ); + cy.get('[placeholder="Specify certificate authority"]').should('not.exist'); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_CONNECTION_TYPE_SELECT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_CONNECTION_TYPE_PLAIN_OPTION); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_CONNECTION_TYPE_ENCRYPTION_OPTION); + + cy.getBySel( + SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_CONNECTION_TYPE_ENCRYPTION_OPTION + ).click(); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_VERIFICATION_MODE_INPUT); + cy.get('[placeholder="Specify certificate authority"]'); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_PASSWORD_OPTION).click(); + + // Verify Partitioning fields + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_PANEL).within(() => { + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_SELECT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_RANDOM_OPTION); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_OPTION); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_ROUND_ROBIN_OPTION); cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_EVENTS_INPUT); + }); - // Verify Hash fields - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_OPTION).click(); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_RANDOM_OPTION).click(); - - // Topics - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_PANEL).within(() => { - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_DEFAULT_TOPIC_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_ADD_ROW_BUTTON); - }); + // Verify Round Robin fields + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_RANDOM_OPTION).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_EVENTS_INPUT); - // Verify one topic processor fields - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_ADD_ROW_BUTTON).click(); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT); - - // Verify additional topic processor fields - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_ADD_ROW_BUTTON).click(); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT); - cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT, 1)); - cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT, 1)); - cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT, 1)); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_DRAG_HANDLE_ICON); - - // Verify remove topic processors - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_REMOVE_ROW_BUTTON).click(); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_REMOVE_ROW_BUTTON).click(); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT).should('not.exist'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT).should('not.exist'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT).should('not.exist'); - - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_PANEL).within(() => { - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_KEY_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_VALUE_INPUT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.disabled'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON).should('be.disabled'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_CLIENT_ID_INPUT); - }); + // Verify Hash fields + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_OPTION).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_RANDOM_OPTION).click(); - // Verify add header - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_KEY_INPUT).type('key'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_VALUE_INPUT).type('value'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.enabled'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON).should('be.disabled'); + // Topics + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_PANEL).within(() => { + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_DEFAULT_TOPIC_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_ADD_ROW_BUTTON); + }); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).click(); - cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.HEADERS_KEY_INPUT, 1)); - cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.HEADERS_VALUE_INPUT, 1)); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.enabled'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON).should('be.enabled'); - - // Verify remove header - cy.getBySel( - getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON, 1) - ).click(); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.enabled'); + // Verify one topic processor fields + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_ADD_ROW_BUTTON).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT); + + // Verify additional topic processor fields + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_ADD_ROW_BUTTON).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT); + cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT, 1)); + cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT, 1)); + cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT, 1)); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_DRAG_HANDLE_ICON); + + // Verify remove topic processors + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_REMOVE_ROW_BUTTON).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_REMOVE_ROW_BUTTON).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT).should('not.exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT).should('not.exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT).should('not.exist'); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_PANEL).within(() => { + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_KEY_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_VALUE_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.disabled'); cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON).should('be.disabled'); - - // Compression - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_CODEC_INPUT).should('not.exist'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_SWITCH).click(); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_LEVEL_INPUT).should('not.exist'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_CODEC_INPUT).select('gzip'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_LEVEL_INPUT).should('exist'); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_LEVEL_INPUT).select('1'); - - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_PANEL).within(() => { - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_ACK_RELIABILITY_SELECT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_CHANNEL_BUFFER_SIZE_SELECT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_TIMEOUT_SELECT); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_REACHABILITY_TIMEOUT_SELECT); - }); - cy.getBySel(SETTINGS_OUTPUTS_KAFKA.KEY_INPUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_CLIENT_ID_INPUT).should( + 'have.value', + 'Elastic' + ); }); - it('displays proper error messages', () => { - selectKafkaOutput(); - cy.getBySel(SETTINGS_SAVE_BTN).click(); - - cy.contains('Name is required'); - cy.contains('URL is required'); - cy.contains('Username is required'); - cy.contains('Password is required'); - cy.contains('Default topic is required'); - shouldDisplayError(SETTINGS_OUTPUTS.NAME_INPUT); - shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_INPUT); - shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT); - shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.TOPICS_DEFAULT_TOPIC_INPUT); + // Verify add header + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_KEY_INPUT).type('key'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_VALUE_INPUT).type('value'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.enabled'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON).should('be.disabled'); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).click(); + cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.HEADERS_KEY_INPUT, 1)); + cy.getBySel(getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.HEADERS_VALUE_INPUT, 1)); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.enabled'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON).should('be.enabled'); + + // Verify remove header + cy.getBySel( + getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON, 1) + ).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_ADD_ROW_BUTTON).should('be.enabled'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_REMOVE_ROW_BUTTON).should('be.disabled'); + + // Compression + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_CODEC_INPUT).should('not.exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_SWITCH).click(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_LEVEL_INPUT).should('not.exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_CODEC_INPUT).select('gzip'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_LEVEL_INPUT).should('exist'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.COMPRESSION_LEVEL_INPUT).select('1'); + + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_PANEL).within(() => { + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_ACK_RELIABILITY_SELECT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_TIMEOUT_SELECT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.BROKER_REACHABILITY_TIMEOUT_SELECT); }); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.KEY_INPUT); }); - // Test buttons presence before accessing output directly via url and delete via api - describe('Output operations', () => { - let kafkaOutputId: string; + it('displays proper error messages', () => { + selectKafkaOutput(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.HEADERS_CLIENT_ID_INPUT).clear(); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.TOPICS_ADD_ROW_BUTTON).click(); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + + cy.contains('Name is required'); + cy.contains('Host is required'); + cy.contains('Username is required'); + cy.contains('Password is required'); + cy.contains('Default topic is required'); + cy.contains('Topic is required'); + cy.contains( + 'Client ID is invalid. Only letters, numbers, dots, underscores, and dashes are allowed.' + ); + cy.contains('Must be a key, value pair i.e. "http.response.code: 200"'); + shouldDisplayError(SETTINGS_OUTPUTS.NAME_INPUT); + shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_INPUT); + shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT); + shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.TOPICS_DEFAULT_TOPIC_INPUT); + shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT); + shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT); + shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.HEADERS_CLIENT_ID_INPUT); + }); + }); - before(() => { - loadKafkaOutput().then((data) => { - kafkaOutputId = data.item.id; - }); - }); + // Test buttons presence before accessing output directly via url and delete via api + describe('Output operations', () => { + let kafkaOutputId: string; - it('opens edit modal', () => { - visit('/app/fleet/settings'); - cy.get(`a[href="/app/fleet/settings/outputs/${kafkaOutputId}"]`) - .parents('tr') - .within(() => { - cy.contains(kafkaOutputBody.name); - cy.contains(kafkaOutputBody.type); - cy.contains(kafkaOutputBody.hosts[0]); - cy.getBySel('editOutputBtn').click(); - cy.url().should('include', `/app/fleet/settings/outputs/${kafkaOutputId}`); - }); - }); - it('delete output', () => { - visit('/app/fleet/settings'); - cy.get(`a[href="/app/fleet/settings/outputs/${kafkaOutputId}"]`) - .parents('tr') - .within(() => { - cy.get('[title="Delete"]').click(); - }); - cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); - cy.get(`a[href="app/fleet/settings/outputs/${kafkaOutputId}"]`).should('not.exist'); + before(() => { + loadKafkaOutput().then((data) => { + kafkaOutputId = data.item.id; }); }); - describe('Form submit', () => { - let kafkaOutputId: string; - - before(() => { - interceptOutputId((id) => { - kafkaOutputId = id; + it('opens edit modal', () => { + visit('/app/fleet/settings'); + cy.get(`a[href="/app/fleet/settings/outputs/${kafkaOutputId}"]`) + .parents('tr') + .within(() => { + cy.contains(kafkaOutputBody.name); + cy.contains(kafkaOutputBody.type); + cy.contains(kafkaOutputBody.hosts[0]); + cy.getBySel('editOutputBtn').click(); + cy.url().should('include', `/app/fleet/settings/outputs/${kafkaOutputId}`); }); - }); + }); + it('delete output', () => { + visit('/app/fleet/settings'); + cy.get(`a[href="/app/fleet/settings/outputs/${kafkaOutputId}"]`) + .parents('tr') + .within(() => { + cy.get('[title="Delete"]').click(); + }); + cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); + cy.get(`a[href="app/fleet/settings/outputs/${kafkaOutputId}"]`).should('not.exist'); + }); + }); - after(() => { - cleanupOutput(kafkaOutputId); + describe('Form submit', () => { + let kafkaOutputId: string; + + before(() => { + interceptOutputId((id) => { + kafkaOutputId = id; }); + }); - it('saves the output', () => { - selectKafkaOutput(); + after(() => { + cleanupOutput(kafkaOutputId); + }); - fillInKafkaOutputForm(); + it('saves the output', () => { + selectKafkaOutput(); - cy.intercept('POST', '**/api/fleet/outputs').as('saveOutput'); + fillInKafkaOutputForm(); - cy.getBySel(SETTINGS_SAVE_BTN).click(); + cy.intercept('POST', '**/api/fleet/outputs').as('saveOutput'); - cy.wait('@saveOutput').then((interception) => { - const responseBody = interception.response?.body; - cy.visit(`/app/fleet/settings/outputs/${responseBody?.item?.id}`); - }); + cy.getBySel(SETTINGS_SAVE_BTN).click(); - validateSavedKafkaOutputForm(); + cy.wait('@saveOutput').then((interception) => { + const responseBody = interception.response?.body; + cy.visit(`/app/fleet/settings/outputs/${responseBody?.item?.id}`); }); + + validateSavedKafkaOutputForm(); }); + }); - describe('Form edit', () => { - let kafkaOutputId: string; + describe('Form edit', () => { + let kafkaOutputId: string; - before(() => { - loadKafkaOutput().then((data) => { - kafkaOutputId = data.item.id; - }); - }); - after(() => { - cleanupOutput(kafkaOutputId); + before(() => { + loadKafkaOutput().then((data) => { + kafkaOutputId = data.item.id; }); + }); + after(() => { + cleanupOutput(kafkaOutputId); + }); - it('edits the output', () => { - visit(`/app/fleet/settings/outputs/${kafkaOutputId}`); + it('edits the output', () => { + visit(`/app/fleet/settings/outputs/${kafkaOutputId}`); - resetKafkaOutputForm(); + resetKafkaOutputForm(); - fillInKafkaOutputForm(); + fillInKafkaOutputForm(); - cy.getBySel(SETTINGS_SAVE_BTN).click(); - cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); - visit(`/app/fleet/settings/outputs/${kafkaOutputId}`); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); + visit(`/app/fleet/settings/outputs/${kafkaOutputId}`); - validateSavedKafkaOutputForm(); - }); + validateSavedKafkaOutputForm(); }); + }); - describe('Form output type change', () => { - let kafkaOutputToESId: string; - let kafkaOutputToLogstashId: string; - let logstashOutputToKafkaId: string; - let esOutputToKafkaId: string; + describe('Form output type change', () => { + let kafkaOutputToESId: string; + let kafkaOutputToLogstashId: string; + let logstashOutputToKafkaId: string; + let esOutputToKafkaId: string; - before(() => { - loadKafkaOutput().then((data) => { - kafkaOutputToESId = data.item.id; - }); - loadKafkaOutput().then((data) => { - kafkaOutputToLogstashId = data.item.id; - }); - loadESOutput().then((data) => { - esOutputToKafkaId = data.item.id; - }); - loadLogstashOutput().then((data) => { - logstashOutputToKafkaId = data.item.id; - }); + before(() => { + loadKafkaOutput().then((data) => { + kafkaOutputToESId = data.item.id; }); - after(() => { - cleanupOutput(kafkaOutputToESId); - cleanupOutput(kafkaOutputToLogstashId); - cleanupOutput(logstashOutputToKafkaId); - cleanupOutput(esOutputToKafkaId); + loadKafkaOutput().then((data) => { + kafkaOutputToLogstashId = data.item.id; }); - it('changes output type from es to kafka', () => { - validateOutputTypeChangeToKafka(esOutputToKafkaId); + loadESOutput().then((data) => { + esOutputToKafkaId = data.item.id; }); - - it('changes output type from logstash to kafka', () => { - validateOutputTypeChangeToKafka(logstashOutputToKafkaId); + loadLogstashOutput().then((data) => { + logstashOutputToKafkaId = data.item.id; }); + }); + after(() => { + cleanupOutput(kafkaOutputToESId); + cleanupOutput(kafkaOutputToLogstashId); + cleanupOutput(logstashOutputToKafkaId); + cleanupOutput(esOutputToKafkaId); + }); + it('changes output type from es to kafka', () => { + validateOutputTypeChangeToKafka(esOutputToKafkaId); + }); - it('changes output type from kafka to es', () => { - visit(`/app/fleet/settings/outputs/${kafkaOutputToESId}`); - cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('elasticsearch'); - cy.getBySel(kafkaOutputFormValues.name.selector).clear().type('kafka_to_es'); + it('changes output type from logstash to kafka', () => { + validateOutputTypeChangeToKafka(logstashOutputToKafkaId); + }); - cy.intercept('PUT', '**/api/fleet/outputs/**').as('saveOutput'); + it('changes output type from kafka to es', () => { + visit(`/app/fleet/settings/outputs/${kafkaOutputToESId}`); + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('elasticsearch'); + cy.getBySel(kafkaOutputFormValues.name.selector).clear().type('kafka_to_es'); + cy.get('[placeholder="Specify host URL"').clear().type('https://localhost:5000'); - cy.getBySel(SETTINGS_SAVE_BTN).click(); - cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); + cy.intercept('PUT', '**/api/fleet/outputs/**').as('saveOutput'); - // wait for the save request to finish to avoid race condition - cy.wait('@saveOutput').then(() => { - visit(`/app/fleet/settings/outputs/${kafkaOutputToESId}`); - }); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); - cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).should('have.value', 'elasticsearch'); - cy.getBySel(kafkaOutputFormValues.name.selector).should('have.value', 'kafka_to_es'); + // wait for the save request to finish to avoid race condition + cy.wait('@saveOutput').then(() => { + visit(`/app/fleet/settings/outputs/${kafkaOutputToESId}`); }); - it('changes output type from kafka to logstash', () => { - visit(`/app/fleet/settings/outputs/${kafkaOutputToLogstashId}`); - cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('logstash'); - cy.getBySel(kafkaOutputFormValues.name.selector).clear().type('kafka_to_logstash'); - cy.get('[placeholder="Specify host"').clear().type('localhost:5000'); - cy.get('[placeholder="Specify ssl certificate"]').clear().type('SSL CERTIFICATE'); - cy.get('[placeholder="Specify certificate key"]').clear().type('SSL KEY'); + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).should('have.value', 'elasticsearch'); + cy.getBySel(kafkaOutputFormValues.name.selector).should('have.value', 'kafka_to_es'); + }); - cy.intercept('PUT', '**/api/fleet/outputs/**').as('saveOutput'); + it('changes output type from kafka to logstash', () => { + visit(`/app/fleet/settings/outputs/${kafkaOutputToLogstashId}`); + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('logstash'); + cy.getBySel(kafkaOutputFormValues.name.selector).clear().type('kafka_to_logstash'); + cy.get('[placeholder="Specify host"').clear().type('localhost:5000'); + cy.get('[placeholder="Specify ssl certificate"]').clear().type('SSL CERTIFICATE'); + cy.get('[placeholder="Specify certificate key"]').clear().type('SSL KEY'); - cy.getBySel(SETTINGS_SAVE_BTN).click(); - cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); + cy.intercept('PUT', '**/api/fleet/outputs/**').as('saveOutput'); - // wait for the save request to finish to avoid race condition - cy.wait('@saveOutput').then(() => { - visit(`/app/fleet/settings/outputs/${kafkaOutputToLogstashId}`); - }); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); - cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).should('have.value', 'logstash'); - cy.getBySel(kafkaOutputFormValues.name.selector).should( - 'have.value', - 'kafka_to_logstash' - ); + // wait for the save request to finish to avoid race condition + cy.wait('@saveOutput').then(() => { + visit(`/app/fleet/settings/outputs/${kafkaOutputToLogstashId}`); }); + + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).should('have.value', 'logstash'); + cy.getBySel(kafkaOutputFormValues.name.selector).should('have.value', 'kafka_to_logstash'); }); - } + }); }); }); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index b21dfa7c4e66c..3b8ffcc63b6f6 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -121,6 +121,8 @@ export const SETTINGS_OUTPUTS = { NAME_INPUT: 'settingsOutputsFlyout.nameInput', TYPE_INPUT: 'settingsOutputsFlyout.typeInput', ADD_HOST_ROW_BTN: 'fleetServerHosts.multiRowInput.addRowButton', + WARNING_KAFKA_CALLOUT: 'settingsOutputsFlyout.kafkaOutputTypeCallout', + WARNING_ELASTICSEARCH_CALLOUT: 'settingsOutputsFlyout.elasticsearchOutputTypeCallout', }; export const getSpecificSelectorId = (selector: string, id: number) => { @@ -136,11 +138,16 @@ export const getSpecificSelectorId = (selector: string, id: number) => { export const SETTINGS_OUTPUTS_KAFKA = { VERSION_SELECT: 'settingsOutputsFlyout.kafkaVersionInput', AUTHENTICATION_SELECT: 'settingsOutputsFlyout.kafkaAuthenticationRadioInput', + AUTHENTICATION_NONE_OPTION: 'kafkaAuthenticationNoneRadioButton', AUTHENTICATION_USERNAME_PASSWORD_OPTION: 'kafkaAuthenticationUsernamePasswordRadioButton', AUTHENTICATION_SSL_OPTION: 'kafkaAuthenticationSSLRadioButton', AUTHENTICATION_KERBEROS_OPTION: 'kafkaAuthenticationKerberosRadioButton', AUTHENTICATION_USERNAME_INPUT: 'settingsOutputsFlyout.kafkaUsernameInput', AUTHENTICATION_PASSWORD_INPUT: 'settingsOutputsFlyout.kafkaPasswordInput', + AUTHENTICATION_VERIFICATION_MODE_INPUT: 'settingsOutputsFlyout.kafkaVerificationModeInput', + AUTHENTICATION_CONNECTION_TYPE_SELECT: 'settingsOutputsFlyout.kafkaConnectionTypeRadioInput', + AUTHENTICATION_CONNECTION_TYPE_PLAIN_OPTION: 'kafkaConnectionTypePlaintextRadioButton', + AUTHENTICATION_CONNECTION_TYPE_ENCRYPTION_OPTION: 'kafkaConnectionTypeEncryptionRadioButton', AUTHENTICATION_SASL_SELECT: 'settingsOutputsFlyout.kafkaSaslInput', AUTHENTICATION_SASL_PLAIN_OPTION: 'kafkaSaslPlainRadioButton', AUTHENTICATION_SASL_SCRAM_256_OPTION: 'kafkaSaslScramSha256RadioButton', @@ -173,7 +180,6 @@ export const SETTINGS_OUTPUTS_KAFKA = { BROKER_PANEL: 'settingsOutputsFlyout.kafkaBrokerSettingsPanel', BROKER_TIMEOUT_SELECT: 'settingsOutputsFlyout.kafkaBrokerTimeoutInput', BROKER_REACHABILITY_TIMEOUT_SELECT: 'settingsOutputsFlyout.kafkaBrokerReachabilityTimeoutInput', - BROKER_CHANNEL_BUFFER_SIZE_SELECT: 'settingsOutputsFlyout.kafkaBrokerChannelBufferSizeInput', BROKER_ACK_RELIABILITY_SELECT: 'settingsOutputsFlyout.kafkaBrokerAckReliabilityInputLabel', KEY_INPUT: 'settingsOutputsFlyout.kafkaKeyInput', }; diff --git a/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts b/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts index c5f7a852197c1..0e018cd301d1b 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts @@ -21,6 +21,8 @@ export const selectKafkaOutput = () => { visit('/app/fleet/settings'); cy.getBySel(SETTINGS_OUTPUTS.ADD_BTN).click(); cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('kafka'); + cy.getBySel(SETTINGS_OUTPUTS.WARNING_KAFKA_CALLOUT); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_PASSWORD_OPTION).click(); }; export const shouldDisplayError = (handler: string) => { @@ -56,7 +58,7 @@ export const kafkaOutputBody = { name: 'kafka_test1', type: 'kafka', is_default: false, - hosts: ['https://example.com'], + hosts: ['example.com:2000'], topics: [{ topic: 'test' }], auth_type: 'user_pass', username: 'kafka', @@ -96,6 +98,10 @@ export const kafkaOutputFormValues = { selector: SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT, value: 'test_password', }, + verificationMode: { + selector: SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_VERIFICATION_MODE_INPUT, + value: 'certificate', + }, hash: { selector: SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_INPUT, value: 'testHash', @@ -110,7 +116,7 @@ export const kafkaOutputFormValues = { }, firstTopicCondition: { selector: SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT, - value: 'testCondition', + value: 'testCondition: abc', }, firstTopicWhen: { selector: SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT, @@ -122,7 +128,7 @@ export const kafkaOutputFormValues = { }, secondTopicCondition: { selector: getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT, 1), - value: 'testCondition1', + value: 'testCondition1: dca', }, secondTopicWhen: { selector: getSpecificSelectorId(SETTINGS_OUTPUTS_KAFKA.TOPICS_WHEN_INPUT, 1), @@ -154,11 +160,7 @@ export const kafkaOutputFormValues = { }, brokerAckReliability: { selector: SETTINGS_OUTPUTS_KAFKA.BROKER_ACK_RELIABILITY_SELECT, - value: 'Do not wait', - }, - brokerChannelBufferSize: { - selector: SETTINGS_OUTPUTS_KAFKA.BROKER_CHANNEL_BUFFER_SIZE_SELECT, - value: '512', + value: '0', }, brokerTimeout: { selector: SETTINGS_OUTPUTS_KAFKA.BROKER_TIMEOUT_SELECT, @@ -185,9 +187,13 @@ export const resetKafkaOutputForm = () => { export const fillInKafkaOutputForm = () => { cy.getBySel(kafkaOutputFormValues.name.selector).type(kafkaOutputFormValues.name.value); - cy.get('[placeholder="Specify host"').clear().type('http://localhost:5000'); + cy.get('[placeholder="Specify host"').clear().type('localhost:5000'); cy.getBySel(kafkaOutputFormValues.username.selector).type(kafkaOutputFormValues.username.value); cy.getBySel(kafkaOutputFormValues.password.selector).type(kafkaOutputFormValues.password.value); + cy.getBySel(kafkaOutputFormValues.verificationMode.selector).select( + kafkaOutputFormValues.verificationMode.value + ); + cy.get('[placeholder="Specify certificate authority"]').clear().type('testCA'); cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SCRAM_256_OPTION).click(); cy.getBySel(SETTINGS_OUTPUTS_KAFKA.PARTITIONING_HASH_OPTION).click(); @@ -249,9 +255,6 @@ export const fillInKafkaOutputForm = () => { cy.getBySel(kafkaOutputFormValues.brokerAckReliability.selector).select( kafkaOutputFormValues.brokerAckReliability.value ); - cy.getBySel(kafkaOutputFormValues.brokerChannelBufferSize.selector).select( - kafkaOutputFormValues.brokerChannelBufferSize.value - ); cy.getBySel(kafkaOutputFormValues.brokerTimeout.selector).select( kafkaOutputFormValues.brokerTimeout.value ); @@ -267,6 +270,9 @@ export const validateSavedKafkaOutputForm = () => { cy.getBySel(selector).should('have.value', value); }); + cy.get('[placeholder="Specify host"').should('have.value', 'localhost:5000'); + cy.get('[placeholder="Specify certificate authority"]').should('have.value', 'testCA'); + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).should('have.value', 'kafka'); cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_SASL_SCRAM_256_OPTION) @@ -285,6 +291,8 @@ export const validateOutputTypeChangeToKafka = (outputId: string) => { visit(`/app/fleet/settings/outputs/${outputId}`); cy.getBySel(kafkaOutputFormValues.name.selector).clear(); cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('kafka'); + cy.getBySel(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_PASSWORD_OPTION).click(); + fillInKafkaOutputForm(); cy.intercept('PUT', '**/api/fleet/outputs/**').as('saveOutput'); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_cloud_formation_modal.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_cloud_formation_modal.tsx index e8300e35d0862..7ed959e40efa0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_cloud_formation_modal.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_cloud_formation_modal.tsx @@ -22,7 +22,7 @@ import { useQuery } from '@tanstack/react-query'; import type { AgentPolicy, PackagePolicy } from '../../../../../types'; import { sendGetEnrollmentAPIKeys, useCreateCloudFormationUrl } from '../../../../../hooks'; -import { getCloudFormationTemplateUrlFromPackagePolicy } from '../../../../../services'; +import { getCloudFormationPropsFromPackagePolicy } from '../../../../../services'; import { CloudFormationGuide } from '../../../../../components'; export const PostInstallCloudFormationModal: React.FunctionComponent<{ @@ -39,13 +39,11 @@ export const PostInstallCloudFormationModal: React.FunctionComponent<{ }) ); - const cloudFormationTemplateUrl = - getCloudFormationTemplateUrlFromPackagePolicy(packagePolicy) || ''; + const cloudFormationProps = getCloudFormationPropsFromPackagePolicy(packagePolicy); const { cloudFormationUrl, error, isError, isLoading } = useCreateCloudFormationUrl({ - cloudFormationTemplateUrl, enrollmentAPIKey: apyKeysData?.data?.items[0]?.api_key, - packagePolicy, + cloudFormationProps, }); return ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 52d02d93c8097..dbb901316cece 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -39,7 +39,7 @@ import type { PackagePolicyFormState } from '../../types'; import { SelectedPolicyTab } from '../../components'; import { useOnSaveNavigate } from '../../hooks'; import { prepareInputPackagePolicyDataset } from '../../services/prepare_input_pkg_policy_dataset'; -import { getCloudFormationTemplateUrlFromPackagePolicy } from '../../../../../services'; +import { getCloudFormationPropsFromPackagePolicy } from '../../../../../services'; async function createAgentPolicy({ packagePolicy, @@ -301,7 +301,7 @@ export function useOnSubmit({ }); const hasCloudFormation = data?.item - ? getCloudFormationTemplateUrlFromPackagePolicy(data.item) + ? getCloudFormationPropsFromPackagePolicy(data.item).templateUrl : false; if (hasCloudFormation) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index d577a1643ac0c..46b367011395c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -43,9 +43,6 @@ const logstashInputsLabels = [ ]; const kafkaInputsLabels = [ - 'Username', - 'Password', - 'SASL Mechanism', 'Partitioning strategy', 'Number of events', 'Default topic', @@ -53,7 +50,6 @@ const kafkaInputsLabels = [ 'Value', 'Broker timeout', 'Broker reachability timeout', - 'Channel buffer size', 'ACK Reliability', 'Key (optional)', ]; @@ -143,7 +139,7 @@ describe('EditOutputFlyout', () => { }); // Does not show logstash inputs - logstashInputsLabels.forEach((label) => { + ['Client SSL certificate key', 'Client SSL certificate'].forEach((label) => { expect(utils.queryByLabelText(label)).toBeNull(); }); }); 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 3f2055e999914..e764e93527b34 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 @@ -73,7 +73,6 @@ export const EditOutputFlyout: React.FunctionComponent = [proxies] ); - const isESOutput = inputs.typeInput.value === outputType.Elasticsearch; const { kafkaOutput: isKafkaOutputEnabled } = ExperimentalFeaturesService.get(); const OUTPUT_TYPE_OPTIONS = [ @@ -249,6 +248,43 @@ export const EditOutputFlyout: React.FunctionComponent = } }; + const renderTypeSpecificWarning = () => { + const isESOutput = inputs.typeInput.value === outputType.Elasticsearch; + const isKafkaOutput = inputs.typeInput.value === outputType.Kafka; + if (!isKafkaOutput && !isESOutput) { + return null; + } + + const generateWarningMessage = () => { + switch (inputs.typeInput.value) { + case outputType.Kafka: + return i18n.translate('xpack.fleet.settings.editOutputFlyout.kafkaOutputTypeCallout', { + defaultMessage: + 'Kafka output is currently not supported on Agents using the Elastic Defend integration.', + }); + default: + case outputType.Elasticsearch: + return i18n.translate('xpack.fleet.settings.editOutputFlyout.esOutputTypeCallout', { + defaultMessage: + 'This output type currently does not support connectivity to a remote Elasticsearch cluster.', + }); + } + }; + return ( + <> + + + + ); + }; + return ( @@ -350,24 +386,7 @@ export const EditOutputFlyout: React.FunctionComponent = } )} /> - {isESOutput && ( - <> - - - - )} + {renderTypeSpecificWarning()} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka.tsx index 222def40b5a75..f6af3270487be 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka.tsx @@ -84,13 +84,13 @@ export const OutputFormKafkaSection: React.FunctionComponent = (props) => helpText={ ), @@ -105,6 +105,7 @@ export const OutputFormKafkaSection: React.FunctionComponent = (props) => + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx index 271f3fe144aca..42a7b66727597 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx @@ -5,14 +5,16 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { + EuiFieldPassword, EuiFieldText, EuiFormRow, EuiPanel, EuiRadioGroup, + EuiSelect, EuiSpacer, EuiTextArea, EuiTitle, @@ -20,7 +22,13 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { MultiRowInput } from '../multi_row_input'; -import { kafkaAuthType, kafkaSaslMechanism } from '../../../../../../../common/constants'; + +import { + kafkaAuthType, + kafkaConnectionType, + kafkaSaslMechanism, + kafkaVerificationModes, +} from '../../../../../../../common/constants'; import type { OutputFormInputsType } from './use_output_form'; @@ -43,6 +51,11 @@ const kafkaSaslOptions = [ ]; const kafkaAuthenticationsOptions = [ + { + id: kafkaAuthType.None, + label: 'None', + 'data-test-subj': 'kafkaAuthenticationNoneRadioButton', + }, { id: kafkaAuthType.Userpass, label: 'Username / Password', @@ -53,11 +66,6 @@ const kafkaAuthenticationsOptions = [ label: 'SSL', 'data-test-subj': 'kafkaAuthenticationSSLRadioButton', }, - { - id: kafkaAuthType.Kerberos, - label: 'Kerberos', - 'data-test-subj': 'kafkaAuthenticationKerberosRadioButton', - }, ]; export const OutputFormKafkaAuthentication: React.FunctionComponent<{ @@ -65,28 +73,56 @@ export const OutputFormKafkaAuthentication: React.FunctionComponent<{ }> = (props) => { const { inputs } = props; + const kafkaVerificationModeOptions = useMemo( + () => + (Object.keys(kafkaVerificationModes) as Array).map( + (key) => { + return { + text: kafkaVerificationModes[key], + label: key, + }; + } + ), + [] + ); + + const kafkaConnectionTypeOptions = useMemo( + () => + (Object.keys(kafkaConnectionType) as Array).map((key) => { + return { + id: kafkaConnectionType[key], + label: key, + 'data-test-subj': `kafkaConnectionType${key}RadioButton`, + }; + }), + [] + ); + const renderAuthentication = () => { switch (inputs.kafkaAuthMethodInput.value) { + case kafkaAuthType.None: + return ( + + } + > + + + ); case kafkaAuthType.Ssl: return ( <> - ); - case kafkaAuthType.Kerberos: - return null; default: case kafkaAuthType.Userpass: return ( @@ -165,7 +199,8 @@ export const OutputFormKafkaAuthentication: React.FunctionComponent<{ } {...inputs.kafkaAuthPasswordInput.formRowProps} > - { + const displayEncryptionSection = + inputs.kafkaConnectionTypeInput.value !== kafkaConnectionType.Plaintext || + inputs.kafkaAuthMethodInput.value !== kafkaAuthType.None; + + if (!displayEncryptionSection) { + return null; + } + + return ( + <> + + + + + + } + > + + + + ); + }; + return ( - - -

- + + +

+ +

+
+ + + -

-
- - - - - {renderAuthentication()} -
+
+ {renderAuthentication()} +
+ {renderEncryptionSection()} + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_broker.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_broker.tsx index f6b0477c81515..2fb40f3e5e376 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_broker.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_broker.tsx @@ -27,14 +27,18 @@ export const OutputFormKafkaBroker: React.FunctionComponent<{ inputs: OutputForm [] ); - const kafkaBrokerChannelBufferSizeOptions = useMemo( - () => - Array.from({ length: 4 }, (_, i) => Math.pow(2, i + 7)).map((buffer) => ({ - text: buffer, - label: `${buffer}`, - })), - [] - ); + const getAckReliabilityLabel = (value: number) => { + switch (value) { + case kafkaAcknowledgeReliabilityLevel.DoNotWait: + return 'No response'; + case kafkaAcknowledgeReliabilityLevel.Replica: + return 'Wait for all replicas to commit'; + default: + case kafkaAcknowledgeReliabilityLevel.Commit: + return 'Wait for local commit'; + } + }; + const kafkaBrokerAckReliabilityOptions = useMemo( () => ( @@ -44,7 +48,7 @@ export const OutputFormKafkaBroker: React.FunctionComponent<{ inputs: OutputForm ).map((key) => { return { text: kafkaAcknowledgeReliabilityLevel[key], - label: kafkaAcknowledgeReliabilityLevel[key], + label: getAckReliabilityLabel(kafkaAcknowledgeReliabilityLevel[key]), }; }), [] @@ -111,28 +115,6 @@ export const OutputFormKafkaBroker: React.FunctionComponent<{ inputs: OutputForm options={kafkaBrokerTimeoutOptions} /> - - } - helpText={ - - } - > - - } > diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_headers.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_headers.tsx index 3f605463a65bc..89ff4b5d0de7a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_headers.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_headers.tsx @@ -114,7 +114,7 @@ export const OutputFormKafkaHeaders: React.FunctionComponent<{ inputs: OutputFor const keyErrors = matchErrorsByIndex(index, 'key'); const valueErrors = matchErrorsByIndex(index, 'value'); return ( - <> +
{index > 0 && } @@ -173,7 +173,7 @@ export const OutputFormKafkaHeaders: React.FunctionComponent<{ inputs: OutputFor /> - +
); })} {displayErrors(globalErrors)} @@ -198,6 +198,7 @@ export const OutputFormKafkaHeaders: React.FunctionComponent<{ inputs: OutputFor defaultMessage="Client ID" /> } + {...inputs.kafkaClientIdInput.formRowProps} > { @@ -64,7 +63,30 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm acc[err.index] = []; } - acc[err.index].push(err.message); + if (!err.condition) { + acc[err.index].push(err.message); + } + + return acc; + }, []); + }, [errors]); + + const indexedConditionErrors = useMemo(() => { + if (!errors) { + return []; + } + return errors.reduce((acc, err) => { + if (err.index === undefined) { + return acc; + } + + if (!acc[err.index]) { + acc[err.index] = []; + } + + if (err.condition) { + acc[err.index].push(err.message); + } return acc; }, []); @@ -97,9 +119,10 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm (index: number) => { const updatedTopics = topics.filter((_, i) => i !== index); indexedErrors.splice(index, 1); + indexedConditionErrors.splice(index, 1); onChange(updatedTopics); }, - [topics, indexedErrors, onChange] + [topics, indexedErrors, indexedConditionErrors, onChange] ); const displayErrors = (errorMessages?: string[]) => { @@ -132,10 +155,13 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm const sourceErrors = indexedErrors[source.index]; indexedErrors.splice(source.index, 1); indexedErrors.splice(destination.index, 0, sourceErrors); + const sourceConditionErrors = indexedConditionErrors[source.index]; + indexedConditionErrors.splice(source.index, 1); + indexedConditionErrors.splice(destination.index, 0, sourceConditionErrors); onChange(items); } }, - [topics, indexedErrors, onChange] + [topics, indexedErrors, indexedConditionErrors, onChange] ); return ( @@ -187,6 +213,7 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm {topics.map((topic, index) => { const topicErrors = indexedErrors[index]; + const topicConditionErrors = indexedConditionErrors[index]; return ( - + 0} + > 0} onChange={(e) => handleTopicProcessorChange(index, 'condition', e.target.value) } @@ -301,6 +333,7 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm <> {topics.map((topic, index) => { const topicErrors = indexedErrors[index]; + const topicConditionErrors = indexedConditionErrors[index]; return ( <> @@ -320,10 +353,15 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm - + 0} + > 0} onChange={(e) => handleTopicProcessorChange(index, 'condition', e.target.value) } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx index 04b07ac9c8b4a..d5da6f553cf89 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx @@ -11,9 +11,71 @@ import { validateYamlConfig, validateCATrustedFingerPrint, validateKafkaHeaders, + validateKafkaHosts, } from './output_form_validators'; describe('Output form validation', () => { + describe('validateKafkaHosts', () => { + it('should not work without any urls', () => { + const res = validateKafkaHosts([]); + + expect(res).toEqual([{ message: 'Host is required' }]); + }); + + it('should work with valid url', () => { + const res = validateKafkaHosts(['test.fr:9200']); + + expect(res).toBeUndefined(); + }); + + it('should work with multiple valid urls', () => { + const res = validateKafkaHosts(['test.fr:9200', 'test2.fr:9200', 'test.fr:9999']); + + expect(res).toBeUndefined(); + }); + + it('should return an error with invalid url', () => { + const res = validateKafkaHosts(['toto']); + + expect(res).toEqual([ + { index: 0, message: 'Invalid format. Expected "host:port" without protocol.' }, + ]); + }); + + it('should return an error with url with defined protocol', () => { + const res = validateKafkaHosts(['https://test.fr:9200']); + + expect(res).toEqual([ + { index: 0, message: 'Invalid format. Expected "host:port" without protocol.' }, + ]); + }); + + it('should return an error with url with invalid port', () => { + const res = validateKafkaHosts(['test.fr:qwerty9200']); + + expect(res).toEqual([ + { index: 0, message: 'Invalid port number. Expected a number between 1 and 65535' }, + ]); + }); + + it('should return an error with multiple invalid urls', () => { + const res = validateKafkaHosts(['toto', 'tata']); + + expect(res).toEqual([ + { index: 0, message: 'Invalid format. Expected "host:port" without protocol.' }, + { index: 1, message: 'Invalid format. Expected "host:port" without protocol.' }, + ]); + }); + it('should return an error with duplicate urls', () => { + const res = validateKafkaHosts(['test.fr:2000', 'test.fr:2000']); + + expect(res).toEqual([ + { index: 0, message: 'Duplicate URL' }, + { index: 1, message: 'Duplicate URL' }, + ]); + }); + }); + describe('validateESHosts', () => { it('should not work without any urls', () => { const res = validateESHosts([]); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx index 8d658e545a854..bbb5b5dda3e91 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx @@ -8,6 +8,73 @@ import { i18n } from '@kbn/i18n'; import { safeLoad } from 'js-yaml'; +export function validateKafkaHosts(value: string[]) { + const res: Array<{ message: string; index?: number }> = []; + const urlIndexes: { [key: string]: number[] } = {}; + + value.forEach((val, idx) => { + if (!val) { + res.push({ + message: i18n.translate('xpack.fleet.settings.outputForm.kafkaHostFieldRequiredError', { + defaultMessage: 'Host is required', + }), + }); + return; + } + + // Split the URL into parts based on ":" + const urlParts = val.split(':'); + if (urlParts.length !== 2 || !urlParts[0] || !urlParts[1]) { + res.push({ + message: i18n.translate('xpack.fleet.settings.outputForm.kafkaHostPortError', { + defaultMessage: 'Invalid format. Expected "host:port" without protocol.', + }), + index: idx, + }); + return; + } + + // Validate that the port is a valid number + const port = parseInt(urlParts[1], 10); + if (isNaN(port) || port < 1 || port > 65535) { + res.push({ + message: i18n.translate('xpack.fleet.settings.outputForm.kafkaPortError', { + defaultMessage: 'Invalid port number. Expected a number between 1 and 65535', + }), + index: idx, + }); + } + + const curIndexes = urlIndexes[val] || []; + urlIndexes[val] = [...curIndexes, idx]; + }); + + Object.values(urlIndexes) + .filter(({ length }) => length > 1) + .forEach((indexes) => { + indexes.forEach((index) => + res.push({ + message: i18n.translate('xpack.fleet.settings.outputForm.kafkaHostDuplicateError', { + defaultMessage: 'Duplicate URL', + }), + index, + }) + ); + }); + + if (value.length === 0) { + res.push({ + message: i18n.translate('xpack.fleet.settings.outputForm.kafkaHostRequiredError', { + defaultMessage: 'Host is required', + }), + }); + } + + if (res.length) { + return res; + } +} + export function validateESHosts(value: string[]) { const res: Array<{ message: string; index?: number }> = []; const urlIndexes: { [key: string]: number[] } = {}; @@ -211,14 +278,31 @@ export function validateKafkaDefaultTopic(value: string) { } } +export function validateKafkaClientId(value: string) { + const regex = /^[A-Za-z0-9._-]+$/; + return regex.test(value) + ? undefined + : [ + i18n.translate('xpack.fleet.settings.outputForm.kafkaClientIdFormattingMessage', { + defaultMessage: + 'Client ID is invalid. Only letters, numbers, dots, underscores, and dashes are allowed.', + }), + ]; +} + export function validateKafkaTopics( topics: Array<{ topic: string; + when?: { + condition?: string; + type?: string; + }; }> ) { const errors: Array<{ message: string; index: number; + condition?: boolean; }> = []; topics.forEach((topic, index) => { @@ -230,6 +314,19 @@ export function validateKafkaTopics( index, }); } + if ( + !topic.when?.condition || + topic.when.condition === '' || + topic.when.condition.split(':').length - 1 !== 1 + ) { + errors.push({ + message: i18n.translate('xpack.fleet.settings.outputForm.kafkaTopicConditionRequired', { + defaultMessage: 'Must be a key, value pair i.e. "http.response.code: 200"', + }), + index, + condition: true, + }); + } }); if (errors.length) { return errors; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx index dbd1b4b0c7a30..bd1891f3ebc12 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx @@ -21,8 +21,10 @@ import { kafkaAcknowledgeReliabilityLevel, kafkaAuthType, kafkaCompressionType, + kafkaConnectionType, kafkaPartitionType, kafkaSaslMechanism, + kafkaVerificationModes, outputType, } from '../../../../../../../common/constants'; @@ -57,6 +59,8 @@ import { validateKafkaHeaders, validateKafkaDefaultTopic, validateKafkaTopics, + validateKafkaClientId, + validateKafkaHosts, } from './output_form_validators'; import { confirmUpdate } from './confirm_update'; @@ -87,7 +91,9 @@ export interface OutputFormInputsType { maxBatchBytes: ReturnType; kafkaHostsInput: ReturnType; kafkaVersionInput: ReturnType; + kafkaVerificationModeInput: ReturnType; kafkaAuthMethodInput: ReturnType; + kafkaConnectionTypeInput: ReturnType; kafkaSaslMechanismInput: ReturnType; kafkaAuthUsernameInput: ReturnType; kafkaAuthPasswordInput: ReturnType; @@ -104,7 +110,6 @@ export interface OutputFormInputsType { kafkaCompressionCodecInput: ReturnType; kafkaBrokerTimeoutInput: ReturnType; kafkaBrokerReachabilityTimeoutInput: ReturnType; - kafkaBrokerChannelBufferSizeInput: ReturnType; kafkaBrokerAckReliabilityInput: ReturnType; kafkaKeyInput: ReturnType; kafkaSslCertificateInput: ReturnType; @@ -278,15 +283,20 @@ export function useOutputForm(onSucess: () => void, output?: Output) { const kafkaHostsInput = useComboInput( 'kafkaHostsComboBox', output?.hosts ?? [], - validateESHosts, + validateKafkaHosts, isDisabled('hosts') ); const kafkaAuthMethodInput = useRadioInput( - kafkaOutput?.auth_type ?? kafkaAuthType.Userpass, + kafkaOutput?.auth_type ?? kafkaAuthType.None, isDisabled('auth_type') ); + const kafkaConnectionTypeInput = useRadioInput( + kafkaOutput?.connection_type ?? kafkaConnectionType.Plaintext, + isDisabled('connection_type') + ); + const kafkaAuthUsernameInput = useInput( kafkaOutput?.username, kafkaAuthMethodInput.value === kafkaAuthType.Userpass ? validateKafkaUsername : undefined, @@ -315,6 +325,12 @@ export function useOutputForm(onSucess: () => void, output?: Output) { isSSLEditable ); + const kafkaVerificationModeInput = useInput( + kafkaOutput?.ssl?.verification_mode ?? kafkaVerificationModes.Full, + undefined, + isSSLEditable + ); + const kafkaSaslMechanismInput = useRadioInput( kafkaOutput?.sasl?.mechanism ?? kafkaSaslMechanism.Plain, isDisabled('sasl') @@ -360,8 +376,8 @@ export function useOutputForm(onSucess: () => void, output?: Output) { ); const kafkaClientIdInput = useInput( - kafkaOutput?.client_id ?? 'Elastic agent', - undefined, + kafkaOutput?.client_id ?? 'Elastic', + validateKafkaClientId, isDisabled('client_id') ); @@ -392,16 +408,10 @@ export function useOutputForm(onSucess: () => void, output?: Output) { isDisabled('timeout') ); - const kafkaBrokerChannelBufferSizeInput = useInput( - `${kafkaOutput?.broker_buffer_size ?? 256}`, - undefined, - isDisabled('broker_buffer_size') - ); - const kafkaBrokerAckReliabilityInput = useInput( - kafkaOutput?.broker_ack_reliability ?? kafkaAcknowledgeReliabilityLevel.Commit, + `${kafkaOutput?.required_acks ?? kafkaAcknowledgeReliabilityLevel.Commit}`, undefined, - isDisabled('broker_ack_reliability') + isDisabled('required_acks') ); const kafkaKeyInput = useInput(kafkaOutput?.key, undefined, isDisabled('key')); @@ -434,7 +444,9 @@ export function useOutputForm(onSucess: () => void, output?: Output) { maxBatchBytes, kafkaVersionInput, kafkaHostsInput, + kafkaVerificationModeInput, kafkaAuthMethodInput, + kafkaConnectionTypeInput, kafkaAuthUsernameInput, kafkaAuthPasswordInput, kafkaSaslMechanismInput, @@ -449,7 +461,6 @@ export function useOutputForm(onSucess: () => void, output?: Output) { kafkaCompressionCodecInput, kafkaBrokerTimeoutInput, kafkaBrokerReachabilityTimeoutInput, - kafkaBrokerChannelBufferSizeInput, kafkaBrokerAckReliabilityInput, kafkaKeyInput, kafkaSslCertificateAuthoritiesInput, @@ -467,6 +478,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { const kafkaHostsValid = kafkaHostsInput.validate(); const kafkaUsernameValid = kafkaAuthUsernameInput.validate(); const kafkaPasswordValid = kafkaAuthPasswordInput.validate(); + const kafkaClientIDValid = kafkaClientIdInput.validate(); const kafkaSslCertificateValid = kafkaSslCertificateInput.validate(); const kafkaSslKeyValid = kafkaSslKeyInput.validate(); const kafkaDefaultTopicValid = kafkaDefaultTopicInput.validate(); @@ -501,7 +513,8 @@ export function useOutputForm(onSucess: () => void, output?: Output) { kafkaHeadersValid && kafkaDefaultTopicValid && kafkaTopicsValid && - additionalYamlConfigValid + additionalYamlConfigValid && + kafkaClientIDValid ); } else { // validate ES @@ -519,6 +532,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { kafkaHostsInput, kafkaAuthUsernameInput, kafkaAuthPasswordInput, + kafkaClientIdInput, kafkaSslCertificateInput, kafkaSslKeyInput, kafkaDefaultTopicInput, @@ -582,7 +596,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { const payload: NewOutput = (() => { const parseIntegerIfStringDefined = (value: string | undefined): number | undefined => { if (value !== undefined) { - const parsedInt = parseInt(value, 10); // Specify the base as 10 for decimal numbers + const parsedInt = parseInt(value, 10); if (!isNaN(parsedInt)) { return parsedInt; } @@ -592,6 +606,10 @@ export function useOutputForm(onSucess: () => void, output?: Output) { switch (typeInput.value) { case outputType.Kafka: + const definedCA = kafkaSslCertificateAuthoritiesInput.value.filter( + (val) => val !== '' + ).length; + return { name: nameInput.value, type: outputType.Kafka, @@ -599,18 +617,26 @@ export function useOutputForm(onSucess: () => void, output?: Output) { is_default: defaultOutputInput.value, is_default_monitoring: defaultMonitoringOutputInput.value, config_yaml: additionalYamlConfigInput.value, - ...(kafkaAuthMethodInput.value === kafkaAuthType.Ssl + ...(kafkaConnectionTypeInput.value !== kafkaConnectionType.Plaintext || + kafkaAuthMethodInput.value !== kafkaAuthType.None ? { ssl: { - certificate: kafkaSslCertificateInput.value, - key: kafkaSslKeyInput.value, - certificate_authorities: kafkaSslCertificateAuthoritiesInput.value.filter( - (val) => val !== '' - ), + ...(definedCA + ? { + certificate_authorities: + kafkaSslCertificateAuthoritiesInput.value.filter((val) => val !== ''), + } + : {}), + ...(kafkaAuthMethodInput.value === kafkaAuthType.Ssl + ? { + certificate: kafkaSslCertificateInput.value, + key: kafkaSslKeyInput.value, + } + : {}), + verification_mode: kafkaVerificationModeInput.value, }, } : {}), - proxy_id: proxyIdValue, client_id: kafkaClientIdInput.value || undefined, @@ -626,8 +652,17 @@ export function useOutputForm(onSucess: () => void, output?: Output) { : {}), auth_type: kafkaAuthMethodInput.value, - ...(kafkaAuthUsernameInput.value ? { username: kafkaAuthUsernameInput.value } : {}), - ...(kafkaAuthPasswordInput.value ? { password: kafkaAuthPasswordInput.value } : {}), + ...(kafkaAuthMethodInput.value === kafkaAuthType.None + ? { connection_type: kafkaConnectionTypeInput.value } + : {}), + ...(kafkaAuthMethodInput.value === kafkaAuthType.Userpass && + kafkaAuthUsernameInput.value + ? { username: kafkaAuthUsernameInput.value } + : {}), + ...(kafkaAuthMethodInput.value === kafkaAuthType.Userpass && + kafkaAuthPasswordInput.value + ? { password: kafkaAuthPasswordInput.value } + : {}), ...(kafkaAuthMethodInput.value === kafkaAuthType.Userpass && kafkaSaslMechanismInput.value ? { sasl: { mechanism: kafkaSaslMechanismInput.value } } @@ -665,10 +700,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { broker_timeout: parseIntegerIfStringDefined( kafkaBrokerReachabilityTimeoutInput.value ), - broker_ack_reliability: kafkaBrokerAckReliabilityInput.value, - broker_buffer_size: parseIntegerIfStringDefined( - kafkaBrokerChannelBufferSizeInput.value - ), + required_acks: parseIntegerIfStringDefined(kafkaBrokerAckReliabilityInput.value), ...shipperParams, } as KafkaOutput; case outputType.Logstash: @@ -752,6 +784,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { compressionLevelInput.value, loadBalanceEnabledInput.value, typeInput.value, + kafkaSslCertificateAuthoritiesInput.value, nameInput.value, kafkaHostsInput.value, defaultOutputInput.value, @@ -760,12 +793,13 @@ export function useOutputForm(onSucess: () => void, output?: Output) { kafkaAuthMethodInput.value, kafkaSslCertificateInput.value, kafkaSslKeyInput.value, - kafkaSslCertificateAuthoritiesInput.value, + kafkaVerificationModeInput.value, kafkaClientIdInput.value, kafkaVersionInput.value, kafkaKeyInput.value, kafkaCompressionCodecInput.value, kafkaCompressionLevelInput.value, + kafkaConnectionTypeInput.value, kafkaAuthUsernameInput.value, kafkaAuthPasswordInput.value, kafkaSaslMechanismInput.value, @@ -779,7 +813,6 @@ export function useOutputForm(onSucess: () => void, output?: Output) { kafkaBrokerTimeoutInput.value, kafkaBrokerReachabilityTimeoutInput.value, kafkaBrokerAckReliabilityInput.value, - kafkaBrokerChannelBufferSizeInput.value, logstashHostsInput.value, sslCertificateInput.value, sslKeyInput.value, diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx index 0edbe5316409b..83031548293f7 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx @@ -10,26 +10,23 @@ import { EuiButton, EuiSpacer, EuiCallOut, EuiSkeletonText } from '@elastic/eui' import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import type { PackagePolicy } from '../../../common'; - import { useCreateCloudFormationUrl } from '../../hooks'; import { CloudFormationGuide } from '../cloud_formation_guide'; +import type { CloudSecurityIntegration } from './types'; + interface Props { enrollmentAPIKey?: string; - cloudFormationTemplateUrl: string; - packagePolicy?: PackagePolicy; + cloudSecurityIntegration: CloudSecurityIntegration; } export const CloudFormationInstructions: React.FunctionComponent = ({ enrollmentAPIKey, - cloudFormationTemplateUrl, - packagePolicy, + cloudSecurityIntegration, }) => { const { isLoading, cloudFormationUrl, error, isError } = useCreateCloudFormationUrl({ enrollmentAPIKey, - cloudFormationTemplateUrl, - packagePolicy, + cloudFormationProps: cloudSecurityIntegration?.cloudFormationProps, }); if (error && isError) { @@ -45,7 +42,7 @@ export const CloudFormationInstructions: React.FunctionComponent = ({ { - if (!agentPolicy) { + const cloudSecurityPackagePolicy = useMemo(() => { + return getCloudSecurityPackagePolicyFromAgentPolicy(agentPolicy); + }, [agentPolicy]); + + const integrationVersion = cloudSecurityPackagePolicy?.package?.version; + + // Fetch the package info to get the CloudFormation template URL only + // if the package policy is a Cloud Security policy + const { data: packageInfoData, isLoading } = useGetPackageInfoByKeyQuery( + FLEET_CLOUD_SECURITY_POSTURE_PACKAGE, + integrationVersion, + { full: true }, + { enabled: Boolean(cloudSecurityPackagePolicy) } + ); + + const cloudSecurityIntegration: CloudSecurityIntegration | undefined = useMemo(() => { + if (!agentPolicy || !cloudSecurityPackagePolicy) { return undefined; } - const integrationType = getCloudSecurityIntegrationTypeFromPackagePolicy(agentPolicy); - const cloudformationUrl = getCloudFormationTemplateUrlFromAgentPolicy(agentPolicy); + const integrationType = cloudSecurityPackagePolicy.inputs?.find((input) => input.enabled) + ?.policy_template as CloudSecurityIntegrationType; + + if (!integrationType) return undefined; + + const cloudFormationTemplateFromAgentPolicy = + getCloudFormationTemplateUrlFromAgentPolicy(agentPolicy); + + // Use the latest CloudFormation template for the current version + // So it guarantee that the template version matches the integration version + // when the integration is upgraded. + // In case it can't find the template for the current version, + // it will fallback to the one from the agent policy. + const cloudFormationTemplateUrl = packageInfoData?.item + ? getCloudFormationTemplateUrlFromPackageInfo(packageInfoData.item, integrationType) + : cloudFormationTemplateFromAgentPolicy; + + const AWS_ACCOUNT_TYPE = 'aws.account_type'; + + const cloudFormationAwsAccountType: CloudSecurityIntegrationAwsAccountType | undefined = + cloudSecurityPackagePolicy?.inputs?.find((input) => input.enabled)?.streams?.[0]?.vars?.[ + AWS_ACCOUNT_TYPE + ]?.value; return { + isLoading, integrationType, - cloudformationUrl, + isCloudFormation: Boolean(cloudFormationTemplateFromAgentPolicy), + cloudFormationProps: { + awsAccountType: cloudFormationAwsAccountType, + templateUrl: cloudFormationTemplateUrl, + }, }; - }, [agentPolicy]); + }, [agentPolicy, packageInfoData?.item, isLoading, cloudSecurityPackagePolicy]); return { cloudSecurityIntegration }; } @@ -97,13 +147,10 @@ const isK8sPackage = (pkg: PackagePolicy) => { return K8S_PACKAGES.has(name); }; -const getCloudSecurityIntegrationTypeFromPackagePolicy = ( - agentPolicy: AgentPolicy -): CloudSecurityIntegrationType | undefined => { - const packagePolicy = agentPolicy?.package_policies?.find( +const getCloudSecurityPackagePolicyFromAgentPolicy = ( + agentPolicy?: AgentPolicy +): PackagePolicy | undefined => { + return agentPolicy?.package_policies?.find( (input) => input.package?.name === FLEET_CLOUD_SECURITY_POSTURE_PACKAGE ); - if (!packagePolicy) return undefined; - return packagePolicy?.inputs?.find((input) => input.enabled) - ?.policy_template as CloudSecurityIntegrationType; }; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx index b602b9fd8931d..d88c9cedb4bcd 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx @@ -80,8 +80,8 @@ export const Instructions = (props: InstructionProps) => { (fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE)); useEffect(() => { - // If we have a cloudFormationTemplateUrl, we want to hide the selection type - if (props.cloudSecurityIntegration?.cloudformationUrl) { + // If we detect a CloudFormation integration, we want to hide the selection type + if (props.cloudSecurityIntegration?.isCloudFormation) { setSelectionType(undefined); } else if (!isIntegrationFlow && showAgentEnrollment) { setSelectionType('radio'); @@ -117,10 +117,7 @@ export const Instructions = (props: InstructionProps) => { {isFleetServerPolicySelected ? ( undefined} /> ) : ( - + )} ); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx index 3977cdd5db576..a750fa48aae7f 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx @@ -200,7 +200,6 @@ export const ManagedSteps: React.FunctionComponent = ({ isK8s, cloudSecurityIntegration, installedPackagePolicy, - cloudFormationTemplateUrl, }) => { const kibanaVersion = useKibanaVersion(); const core = useStartServices(); @@ -247,14 +246,13 @@ export const ManagedSteps: React.FunctionComponent = ({ ); } - if (cloudFormationTemplateUrl) { + if (cloudSecurityIntegration?.isCloudFormation) { steps.push( InstallCloudFormationManagedAgentStep({ apiKeyData, selectedApiKeyId, enrollToken, - cloudFormationTemplateUrl, - agentPolicy, + cloudSecurityIntegration, }) ); } else { @@ -314,7 +312,6 @@ export const ManagedSteps: React.FunctionComponent = ({ link, agentDataConfirmed, installedPackagePolicy, - cloudFormationTemplateUrl, ]); return ; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_cloud_formation_managed_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_cloud_formation_managed_agent_step.tsx index 75fec5be125f5..7826d1648ae64 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_cloud_formation_managed_agent_step.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_cloud_formation_managed_agent_step.tsx @@ -11,46 +11,38 @@ import { i18n } from '@kbn/i18n'; import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; -import type { AgentPolicy } from '../../../../common'; - import type { GetOneEnrollmentAPIKeyResponse } from '../../../../common/types/rest_spec/enrollment_api_key'; import { CloudFormationInstructions } from '../cloud_formation_instructions'; -import { FLEET_CLOUD_SECURITY_POSTURE_PACKAGE } from '../../../../common'; + +import type { CloudSecurityIntegration } from '../types'; export const InstallCloudFormationManagedAgentStep = ({ selectedApiKeyId, apiKeyData, enrollToken, isComplete, - cloudFormationTemplateUrl, - agentPolicy, + cloudSecurityIntegration, }: { selectedApiKeyId?: string; apiKeyData?: GetOneEnrollmentAPIKeyResponse | null; enrollToken?: string; isComplete?: boolean; - cloudFormationTemplateUrl: string; - agentPolicy?: AgentPolicy; + cloudSecurityIntegration?: CloudSecurityIntegration | undefined; }): EuiContainedStepProps => { const nonCompleteStatus = selectedApiKeyId ? undefined : 'disabled'; const status = isComplete ? 'complete' : nonCompleteStatus; - const cloudSecurityPackagePolicy = agentPolicy?.package_policies?.find( - (p) => p.package?.name === FLEET_CLOUD_SECURITY_POSTURE_PACKAGE - ); - return { status, title: i18n.translate('xpack.fleet.agentEnrollment.cloudFormation.stepEnrollAndRunAgentTitle', { defaultMessage: 'Install Elastic Agent on your cloud', }), children: - selectedApiKeyId && apiKeyData ? ( + selectedApiKeyId && apiKeyData && cloudSecurityIntegration ? ( ) : ( diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index 79c8029e4ec01..f1cd44ea5348b 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -16,13 +16,21 @@ export type K8sMode = | 'IS_KUBERNETES_MULTIPAGE'; export type CloudSecurityIntegrationType = 'kspm' | 'vuln_mgmt' | 'cspm'; +export type CloudSecurityIntegrationAwsAccountType = 'single-account' | 'organization-account'; export type FlyoutMode = 'managed' | 'standalone'; export type SelectionType = 'tabs' | 'radio' | undefined; +export interface CloudFormationProps { + templateUrl: string | undefined; + awsAccountType: CloudSecurityIntegrationAwsAccountType | undefined; +} + export interface CloudSecurityIntegration { integrationType: CloudSecurityIntegrationType | undefined; - cloudformationUrl: string | undefined; + isLoading: boolean; + isCloudFormation: boolean; + cloudFormationProps?: CloudFormationProps; } export interface BaseProps { @@ -65,5 +73,4 @@ export interface InstructionProps extends BaseProps { setSelectedAPIKeyId: (key?: string) => void; fleetServerHosts: string[]; fleetProxy?: FleetProxy; - cloudFormationTemplateUrl?: string; } diff --git a/x-pack/plugins/fleet/public/hooks/use_create_cloud_formation_url.ts b/x-pack/plugins/fleet/public/hooks/use_create_cloud_formation_url.ts index a28b0f46adb41..45e5b259afd15 100644 --- a/x-pack/plugins/fleet/public/hooks/use_create_cloud_formation_url.ts +++ b/x-pack/plugins/fleet/public/hooks/use_create_cloud_formation_url.ts @@ -7,34 +7,27 @@ import { i18n } from '@kbn/i18n'; -import type { PackagePolicy, PackagePolicyInput } from '../../common'; +import type { + CloudFormationProps, + CloudSecurityIntegrationAwsAccountType, +} from '../components/agent_enrollment_flyout/types'; import { useKibanaVersion } from './use_kibana_version'; import { useGetSettings } from './use_request'; -type AwsAccountType = 'single_account' | 'organization_account'; - -const CLOUDBEAT_AWS = 'cloudbeat/cis_aws'; - -const getAwsAccountType = (input?: PackagePolicyInput): AwsAccountType | undefined => - input?.streams[0].vars?.['aws.account_type']?.value; +const CLOUD_FORMATION_DEFAULT_ACCOUNT_TYPE = 'single-account'; export const useCreateCloudFormationUrl = ({ enrollmentAPIKey, - cloudFormationTemplateUrl, - packagePolicy, + cloudFormationProps, }: { enrollmentAPIKey: string | undefined; - cloudFormationTemplateUrl: string; - packagePolicy?: PackagePolicy; + cloudFormationProps: CloudFormationProps | undefined; }) => { const { data, isLoading } = useGetSettings(); const kibanaVersion = useKibanaVersion(); - const awsInput = packagePolicy?.inputs?.find((input) => input.type === CLOUDBEAT_AWS); - const awsAccountType = getAwsAccountType(awsInput) || ''; - let isError = false; let error: string | undefined; @@ -56,13 +49,13 @@ export const useCreateCloudFormationUrl = ({ } const cloudFormationUrl = - enrollmentAPIKey && fleetServerHost && cloudFormationTemplateUrl + enrollmentAPIKey && fleetServerHost && cloudFormationProps?.templateUrl ? createCloudFormationUrl( - cloudFormationTemplateUrl, + cloudFormationProps?.templateUrl, enrollmentAPIKey, fleetServerHost, kibanaVersion, - awsAccountType + cloudFormationProps?.awsAccountType ) : undefined; @@ -79,7 +72,7 @@ const createCloudFormationUrl = ( enrollmentToken: string, fleetUrl: string, kibanaVersion: string, - awsAccountType: string + awsAccountType: CloudSecurityIntegrationAwsAccountType | undefined ) => { let cloudFormationUrl; @@ -89,8 +82,15 @@ const createCloudFormationUrl = ( .replace('KIBANA_VERSION', kibanaVersion); if (cloudFormationUrl.includes('ACCOUNT_TYPE')) { - cloudFormationUrl = cloudFormationUrl.replace('ACCOUNT_TYPE', awsAccountType); + cloudFormationUrl = cloudFormationUrl.replace( + 'ACCOUNT_TYPE', + getAwsAccountType(awsAccountType) + ); } return new URL(cloudFormationUrl).toString(); }; + +const getAwsAccountType = (awsAccountType: CloudSecurityIntegrationAwsAccountType | undefined) => { + return awsAccountType ? awsAccountType : CLOUD_FORMATION_DEFAULT_ACCOUNT_TYPE; +}; diff --git a/x-pack/plugins/fleet/public/hooks/use_input.ts b/x-pack/plugins/fleet/public/hooks/use_input.ts index a48e3075d7a42..dbc60eafda833 100644 --- a/x-pack/plugins/fleet/public/hooks/use_input.ts +++ b/x-pack/plugins/fleet/public/hooks/use_input.ts @@ -86,6 +86,16 @@ export function useInput( export function useRadioInput(defaultValue: string, disabled = false) { const [value, setValue] = useState(defaultValue); + const [hasChanged, setHasChanged] = useState(false); + + useEffect(() => { + if (hasChanged) { + return; + } + if (value !== defaultValue) { + setHasChanged(true); + } + }, [hasChanged, value, defaultValue]); const onChange = useCallback(setValue, [setValue]); @@ -97,6 +107,7 @@ export function useRadioInput(defaultValue: string, disabled = false) { }, setValue, value, + hasChanged, }; } @@ -137,11 +148,15 @@ export function useSwitchInput(defaultValue = false, disabled = false) { function useCustomInput( id: string, defaultValue: T, - validate?: (value: T) => Array<{ message: string; index?: number }> | undefined, + validate?: ( + value: T + ) => Array<{ message: string; index?: number; condition?: boolean }> | undefined, disabled = false ) { const [value, setValue] = useState(defaultValue); - const [errors, setErrors] = useState | undefined>(); + const [errors, setErrors] = useState< + Array<{ message: string; index?: number; condition?: boolean }> | undefined + >(); const [hasChanged, setHasChanged] = useState(false); useEffect(() => { @@ -237,7 +252,9 @@ type Topic = Array<{ export function useTopicsInput( id: string, defaultValue: Topic = [], - validate?: (value: Topic) => Array<{ message: string; index: number }> | undefined, + validate?: ( + value: Topic + ) => Array<{ message: string; index: number; condition?: boolean }> | undefined, disabled = false ) { return useCustomInput(id, defaultValue, validate, disabled); diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index cc381c20ecd97..df8ceb8a4c2e2 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -105,6 +105,13 @@ export const useGetPackageInfoByKeyQuery = ( ignoreUnverified?: boolean; prerelease?: boolean; full?: boolean; + }, + // Additional options for the useQuery hook + queryOptions: { + // If enabled is false, the query will not be fetched + enabled?: boolean; + } = { + enabled: true, } ) => { const confirmOpenUnverified = useConfirmOpenUnverified(); @@ -112,15 +119,18 @@ export const useGetPackageInfoByKeyQuery = ( options?.ignoreUnverified ); - const response = useQuery([pkgName, pkgVersion, options], () => - sendRequestForRq({ - path: epmRouteService.getInfoPath(pkgName, pkgVersion), - method: 'get', - query: { - ...options, - ...(ignoreUnverifiedQueryParam && { ignoreUnverified: ignoreUnverifiedQueryParam }), - }, - }) + const response = useQuery( + [pkgName, pkgVersion, options], + () => + sendRequestForRq({ + path: epmRouteService.getInfoPath(pkgName, pkgVersion), + method: 'get', + query: { + ...options, + ...(ignoreUnverifiedQueryParam && { ignoreUnverified: ignoreUnverifiedQueryParam }), + }, + }), + { enabled: queryOptions.enabled } ); const confirm = async () => { diff --git a/x-pack/plugins/fleet/public/services/get_cloud_formation_props_from_package_policy.test.ts b/x-pack/plugins/fleet/public/services/get_cloud_formation_props_from_package_policy.test.ts new file mode 100644 index 0000000000000..f84d5c839dd40 --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_formation_props_from_package_policy.test.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { getCloudFormationPropsFromPackagePolicy } from './get_cloud_formation_props_from_package_policy'; + +describe('getCloudFormationPropsFromPackagePolicy', () => { + test('returns empty CloudFormationProps when packagePolicy is undefined', () => { + const result = getCloudFormationPropsFromPackagePolicy(undefined); + expect(result).toEqual({ + templateUrl: undefined, + awsAccountType: undefined, + }); + }); + + test('returns empty CloudFormationProps when packagePolicy has no inputs', () => { + const packagePolicy = { otherProperty: 'value' }; + // @ts-expect-error + const result = getCloudFormationPropsFromPackagePolicy(packagePolicy); + expect(result).toEqual({ + templateUrl: undefined, + awsAccountType: undefined, + }); + }); + + test('returns empty CloudFormationProps when no enabled input has a cloudFormationTemplateUrl', () => { + const packagePolicy = { + inputs: [ + { enabled: false, config: { cloud_formation_template_url: { value: 'template1' } } }, + { enabled: false, config: { cloud_formation_template_url: { value: 'template2' } } }, + ], + }; + // @ts-expect-error + const result = getCloudFormationPropsFromPackagePolicy(packagePolicy); + expect(result).toEqual({ + templateUrl: undefined, + awsAccountType: undefined, + }); + }); + + test('returns the cloudFormationTemplateUrl and awsAccountType when found in the enabled input', () => { + const packagePolicy = { + inputs: [ + { + enabled: true, + config: { cloud_formation_template_url: { value: 'template1' } }, + streams: [ + { + vars: { + ['aws.account_type']: { value: 'aws_account_type_value' }, + }, + }, + ], + }, + { + enabled: false, + config: { cloud_formation_template_url: { value: 'template2' } }, + streams: [ + { + vars: { + ['aws.account_type']: { value: 'aws_account_type_value2' }, + }, + }, + ], + }, + ], + }; + // @ts-expect-error + const result = getCloudFormationPropsFromPackagePolicy(packagePolicy); + expect(result).toEqual({ + templateUrl: 'template1', + awsAccountType: 'aws_account_type_value', + }); + }); + + test('returns the first cloudFormationTemplateUrl and awsAccountType when multiple enabled inputs have them', () => { + const packagePolicy = { + inputs: [ + { + enabled: true, + config: { + cloud_formation_template_url: { value: 'template1' }, + }, + streams: [ + { + vars: { + ['aws.account_type']: { value: 'aws_account_type_value1' }, + }, + }, + { + vars: { + ['aws.account_type']: { value: 'aws_account_type_value2' }, + }, + }, + ], + }, + { + enabled: true, + config: { + cloud_formation_template_url: { value: 'template2' }, + }, + streams: [ + { + vars: { + ['aws.account_type']: { value: 'aws_account_type_value1' }, + }, + }, + { + vars: { + ['aws.account_type']: { value: 'aws_account_type_value2' }, + }, + }, + ], + }, + ], + }; + // @ts-expect-error + const result = getCloudFormationPropsFromPackagePolicy(packagePolicy); + expect(result).toEqual({ + templateUrl: 'template1', + awsAccountType: 'aws_account_type_value1', + }); + }); +}); diff --git a/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.ts b/x-pack/plugins/fleet/public/services/get_cloud_formation_props_from_package_policy.ts similarity index 52% rename from x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.ts rename to x-pack/plugins/fleet/public/services/get_cloud_formation_props_from_package_policy.ts index 598e71709fdc7..b56659b21db99 100644 --- a/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.ts +++ b/x-pack/plugins/fleet/public/services/get_cloud_formation_props_from_package_policy.ts @@ -5,15 +5,23 @@ * 2.0. */ +import type { + CloudFormationProps, + CloudSecurityIntegrationAwsAccountType, +} from '../components/agent_enrollment_flyout/types'; import type { PackagePolicy } from '../types'; +const AWS_ACCOUNT_TYPE = 'aws.account_type'; + /** * Get the cloud formation template url from a package policy * It looks for a config with a cloud_formation_template_url object present in * the enabled inputs of the package policy */ -export const getCloudFormationTemplateUrlFromPackagePolicy = (packagePolicy?: PackagePolicy) => { - const cloudFormationTemplateUrl = packagePolicy?.inputs?.reduce((accInput, input) => { +export const getCloudFormationPropsFromPackagePolicy = ( + packagePolicy?: PackagePolicy +): CloudFormationProps => { + const templateUrl = packagePolicy?.inputs?.reduce((accInput, input) => { if (accInput !== '') { return accInput; } @@ -23,5 +31,12 @@ export const getCloudFormationTemplateUrlFromPackagePolicy = (packagePolicy?: Pa return accInput; }, ''); - return cloudFormationTemplateUrl !== '' ? cloudFormationTemplateUrl : undefined; + const awsAccountType: CloudSecurityIntegrationAwsAccountType | undefined = + packagePolicy?.inputs?.find((input) => input.enabled)?.streams?.[0]?.vars?.[AWS_ACCOUNT_TYPE] + ?.value; + + return { + templateUrl: templateUrl !== '' ? templateUrl : undefined, + awsAccountType, + }; }; diff --git a/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_info.test.ts b/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_info.test.ts new file mode 100644 index 0000000000000..8ed2fb3ae389a --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_info.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { getCloudFormationTemplateUrlFromPackageInfo } from './get_cloud_formation_template_url_from_package_info'; + +describe('getCloudFormationTemplateUrlFromPackageInfo', () => { + test('returns undefined when packageInfo is undefined', () => { + const result = getCloudFormationTemplateUrlFromPackageInfo(undefined, 'test'); + expect(result).toBeUndefined(); + }); + + test('returns undefined when packageInfo has no policy_templates', () => { + const packageInfo = { inputs: [] }; + // @ts-expect-error + const result = getCloudFormationTemplateUrlFromPackageInfo(packageInfo, 'test'); + expect(result).toBeUndefined(); + }); + + test('returns undefined when integrationType is not found in policy_templates', () => { + const packageInfo = { policy_templates: [{ name: 'template1' }, { name: 'template2' }] }; + // @ts-expect-error + const result = getCloudFormationTemplateUrlFromPackageInfo(packageInfo, 'nonExistentTemplate'); + expect(result).toBeUndefined(); + }); + + test('returns undefined when no input in the policy template has a cloudFormationTemplate', () => { + const packageInfo = { + policy_templates: [ + { + name: 'template1', + inputs: [ + { name: 'input1', vars: [] }, + { name: 'input2', vars: [{ name: 'var1', default: 'value1' }] }, + ], + }, + ], + }; + // @ts-expect-error + const result = getCloudFormationTemplateUrlFromPackageInfo(packageInfo, 'template1'); + expect(result).toBeUndefined(); + }); + + test('returns the cloudFormationTemplate from the policy template', () => { + const packageInfo = { + policy_templates: [ + { + name: 'template1', + inputs: [ + { name: 'input1', vars: [] }, + { + name: 'input2', + vars: [{ name: 'cloud_formation_template', default: 'cloud_formation_template_url' }], + }, + ], + }, + ], + }; + // @ts-expect-error + const result = getCloudFormationTemplateUrlFromPackageInfo(packageInfo, 'template1'); + expect(result).toBe('cloud_formation_template_url'); + }); +}); diff --git a/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_info.ts b/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_info.ts new file mode 100644 index 0000000000000..4f5381ccedb3f --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_info.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PackageInfo } from '../types'; + +/** + * Get the cloud formation template url from the PackageInfo + * It looks for a input var with a object containing cloud_formation_template_url present in + * the package_policies inputs of the given integration type + */ +export const getCloudFormationTemplateUrlFromPackageInfo = ( + packageInfo: PackageInfo | undefined, + integrationType: string +): string | undefined => { + if (!packageInfo?.policy_templates) return undefined; + + const policyTemplate = packageInfo.policy_templates.find((p) => p.name === integrationType); + if (!policyTemplate) return undefined; + + if ('inputs' in policyTemplate) { + const cloudFormationTemplate = policyTemplate.inputs?.reduce((acc, input): string => { + if (!input.vars) return acc; + const template = input.vars.find((v) => v.name === 'cloud_formation_template')?.default; + return template ? String(template) : acc; + }, ''); + return cloudFormationTemplate !== '' ? cloudFormationTemplate : undefined; + } +}; diff --git a/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.test.ts b/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.test.ts deleted file mode 100644 index 523641b10eb1b..0000000000000 --- a/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { getCloudFormationTemplateUrlFromPackagePolicy } from './get_cloud_formation_template_url_from_package_policy'; - -describe('getCloudFormationTemplateUrlFromPackagePolicy', () => { - test('returns undefined when packagePolicy is undefined', () => { - const result = getCloudFormationTemplateUrlFromPackagePolicy(undefined); - expect(result).toBeUndefined(); - }); - - test('returns undefined when packagePolicy is defined but inputs are empty', () => { - const packagePolicy = { inputs: [] }; - // @ts-expect-error - const result = getCloudFormationTemplateUrlFromPackagePolicy(packagePolicy); - expect(result).toBeUndefined(); - }); - - test('returns undefined when no enabled input has a cloudFormationTemplateUrl', () => { - const packagePolicy = { - inputs: [ - { enabled: false, config: { cloud_formation_template_url: { value: 'template1' } } }, - { enabled: false, config: { cloud_formation_template_url: { value: 'template2' } } }, - ], - }; - // @ts-expect-error - const result = getCloudFormationTemplateUrlFromPackagePolicy(packagePolicy); - expect(result).toBeUndefined(); - }); - - test('returns the cloudFormationTemplateUrl of the first enabled input', () => { - const packagePolicy = { - inputs: [ - { enabled: false, config: { cloud_formation_template_url: { value: 'template1' } } }, - { enabled: true, config: { cloud_formation_template_url: { value: 'template2' } } }, - { enabled: true, config: { cloud_formation_template_url: { value: 'template3' } } }, - ], - }; - // @ts-expect-error - const result = getCloudFormationTemplateUrlFromPackagePolicy(packagePolicy); - expect(result).toBe('template2'); - }); - - test('returns the cloudFormationTemplateUrl of the first enabled input and ignores subsequent inputs', () => { - const packagePolicy = { - inputs: [ - { enabled: true, config: { cloud_formation_template_url: { value: 'template1' } } }, - { enabled: true, config: { cloud_formation_template_url: { value: 'template2' } } }, - { enabled: true, config: { cloud_formation_template_url: { value: 'template3' } } }, - ], - }; - // @ts-expect-error - const result = getCloudFormationTemplateUrlFromPackagePolicy(packagePolicy); - expect(result).toBe('template1'); - }); - - // Add more test cases as needed -}); diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 1da10c7384cdc..44bf6b965742c 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -48,5 +48,6 @@ export { isPackageUpdatable } from './is_package_updatable'; export { pkgKeyFromPackageInfo } from './pkg_key_from_package_info'; export { createExtensionRegistrationCallback } from './ui_extensions'; export { incrementPolicyName } from './increment_policy_name'; -export { getCloudFormationTemplateUrlFromPackagePolicy } from './get_cloud_formation_template_url_from_package_policy'; +export { getCloudFormationPropsFromPackagePolicy } from './get_cloud_formation_props_from_package_policy'; export { getCloudFormationTemplateUrlFromAgentPolicy } from './get_cloud_formation_template_url_from_agent_policy'; +export { getCloudFormationTemplateUrlFromPackageInfo } from './get_cloud_formation_template_url_from_package_info'; diff --git a/x-pack/plugins/fleet/server/config.ts b/x-pack/plugins/fleet/server/config.ts index 14e5a86aa73ad..9726837375eed 100644 --- a/x-pack/plugins/fleet/server/config.ts +++ b/x-pack/plugins/fleet/server/config.ts @@ -139,7 +139,6 @@ export const config: PluginConfigDescriptor = { disableRegistryVersionCheck: schema.boolean({ defaultValue: false }), allowAgentUpgradeSourceUri: schema.boolean({ defaultValue: false }), bundledPackageLocation: schema.string({ defaultValue: DEFAULT_BUNDLED_PACKAGE_LOCATION }), - testSecretsIndex: schema.maybe(schema.string()), }), packageVerification: schema.object({ gpgKeyPath: schema.string({ defaultValue: DEFAULT_GPG_KEY_PATH }), diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index 356aaa0e0cf8e..807eef8ba9917 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -78,7 +78,7 @@ export { // Message signing service MESSAGE_SIGNING_SERVICE_API_ROUTES, // secrets - SECRETS_INDEX, + SECRETS_ENDPOINT_PATH, } from '../../common/constants'; export { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 43e0069d3be48..b7013bf43ff84 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -25,6 +25,8 @@ import { UNINSTALL_TOKENS_SAVED_OBJECT_TYPE, } from '../constants'; +import { migrateOutputEvictionsFromV8100, migrateOutputToV8100 } from './migrations/to_v8_10_0'; + import { migrateSyntheticsPackagePolicyToV8100 } from './migrations/synthetics/to_v8_10_0'; import { migratePackagePolicyEvictionsFromV8100 } from './migrations/security_solution/to_v8_10_0'; @@ -177,6 +179,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ compression_level: { type: 'integer' }, client_id: { type: 'keyword' }, auth_type: { type: 'keyword' }, + connection_type: { type: 'keyword' }, username: { type: 'keyword' }, password: { type: 'text', index: false }, sasl: { @@ -229,6 +232,29 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ broker_timeout: { type: 'integer' }, broker_ack_reliability: { type: 'text' }, broker_buffer_size: { type: 'integer' }, + required_acks: { type: 'integer' }, + channel_buffer_size: { type: 'integer' }, + }, + }, + modelVersions: { + '1': { + changes: [ + { + type: 'mappings_deprecation', + deprecatedMappings: [ + 'broker_ack_reliability', + 'broker_buffer_size', + 'channel_buffer_size', + ], + }, + { + type: 'data_backfill', + backfillFn: migrateOutputToV8100, + }, + ], + schemas: { + forwardCompatibility: migrateOutputEvictionsFromV8100, + }, }, }, migrations: { @@ -528,6 +554,24 @@ export function registerEncryptedSavedObjects( 'config_yaml', 'is_preconfigured', 'proxy_id', + 'version', + 'key', + 'compression', + 'compression_level', + 'client_id', + 'auth_type', + 'connection_type', + 'username', + 'sasl', + 'partition', + 'random', + 'round_robin', + 'hash', + 'topics', + 'headers', + 'timeout', + 'broker_timeout', + 'required_acks', ]), }); // Encrypted saved objects diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_10_0.test.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_10_0.test.ts new file mode 100644 index 0000000000000..92b8c2c2d34b6 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_10_0.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectModelTransformationContext } from '@kbn/core-saved-objects-server'; + +import { + migrateOutputToV8100 as migration, + migrateOutputEvictionsFromV8100 as eviction, +} from './to_v8_10_0'; + +describe('8.10.0 migration', () => { + describe('Migrate output to v8.10.0', () => { + const outputDoc = (connectionType = {}) => ({ + id: 'mock-saved-object-id', + attributes: { + id: 'id', + name: 'Test', + type: 'kafka' as const, + is_default: false, + is_default_monitoring: false, + hosts: ['localhost:9092'], + ca_sha256: 'sha', + ca_trusted_fingerprint: 'fingerprint', + version: '7.10.0', + key: 'key', + compression: 'gzip' as const, + compression_level: 4, + client_id: 'Elastic', + auth_type: 'none' as const, + ...connectionType, + topics: [{ topic: 'topic' }], + }, + type: 'nested', + }); + + it('adds connection type field to output and sets it to plaintext', () => { + const initialDoc = outputDoc({}); + + const migratedDoc = outputDoc({ + connection_type: 'plaintext', + }); + + expect(migration(initialDoc, {} as SavedObjectModelTransformationContext)).toEqual({ + attributes: migratedDoc.attributes, + }); + }); + + it('removes connection type field from output', () => { + const initialDoc = outputDoc({ + connection_type: 'plaintext', + }); + + const migratedDoc = outputDoc({}); + + expect(eviction(initialDoc.attributes)).toEqual(migratedDoc.attributes); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_10_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_10_0.ts new file mode 100644 index 0000000000000..fc0ebda76086b --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_10_0.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + SavedObjectModelDataBackfillFn, + SavedObjectUnsanitizedDoc, +} from '@kbn/core-saved-objects-server'; + +import type { SavedObjectModelVersionForwardCompatibilityFn } from '@kbn/core-saved-objects-server'; + +import { omit } from 'lodash'; + +import type { Output } from '../../../common'; + +export const migrateOutputToV8100: SavedObjectModelDataBackfillFn = (outputDoc) => { + const updatedOutputDoc: SavedObjectUnsanitizedDoc = outputDoc; + + if (updatedOutputDoc.attributes.type === 'kafka') { + updatedOutputDoc.attributes.connection_type = 'plaintext'; + } + + return { + attributes: updatedOutputDoc.attributes, + }; +}; + +export const migrateOutputEvictionsFromV8100: SavedObjectModelVersionForwardCompatibilityFn = ( + unknownAttributes +) => { + const attributes = unknownAttributes as Output; + if (attributes.type !== 'kafka') { + return attributes; + } + + let updatedAttributes = attributes; + + updatedAttributes = omit(updatedAttributes, ['connection_type']); + + return updatedAttributes; +}; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 1da8187744091..d50e12541063a 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -291,7 +291,6 @@ export function transformOutputToFullPolicyOutput( key, compression, compression_level, - auth_type, username, password, sasl, @@ -303,31 +302,69 @@ export function transformOutputToFullPolicyOutput( headers, timeout, broker_timeout, - broker_buffer_size, - broker_ack_reliability, + required_acks, } = output; - /* eslint-enable @typescript-eslint/naming-convention */ + const transformPartition = () => { + if (!partition) return {}; + switch (partition) { + case 'random': + return { + random: { + ...(random?.group_events + ? { group_events: random.group_events } + : { group_events: 1 }), + }, + }; + case 'round_robin': + return { + round_robin: { + ...(round_robin?.group_events + ? { group_events: round_robin.group_events } + : { group_events: 1 }), + }, + }; + case 'hash': + default: + return { hash: { ...(hash?.hash ? { hash: hash.hash } : { hash: '' }) } }; + } + }; + + /* eslint-enable @typescript-eslint/naming-convention */ kafkaData = { client_id, version, key, compression, compression_level, - auth_type, - username, - password, - sasl, - partition, - random, - round_robin, - hash, - topics, - headers, + ...(username ? { username } : {}), + ...(password ? { password } : {}), + ...(sasl ? { sasl } : {}), + partition: transformPartition(), + topics: (topics ?? []).map((topic) => { + const { topic: topicName, ...rest } = topic; + const whenKeys = Object.keys(rest); + + if (whenKeys.length === 0) { + return { topic: topicName }; + } + if (rest.when && rest.when.condition) { + const [keyName, value] = rest.when.condition.split(':'); + + return { + topic: topicName, + when: { + [rest.when.type as string]: { + [keyName.replace(/\s/g, '')]: value.replace(/\s/g, ''), + }, + }, + }; + } + }), + headers: (headers ?? []).filter((item) => item.key !== '' || item.value !== ''), timeout, broker_timeout, - broker_buffer_size, - broker_ack_reliability, + required_acks, }; } diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index e9a4e255e9ea1..ec0cbab539bcd 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -320,6 +320,7 @@ export async function installKibanaSavedObjects({ readStream: createListStream(toBeSavedObjects), createNewCopies: false, refresh: false, + managed: true, }) ); @@ -371,6 +372,7 @@ export async function installKibanaSavedObjects({ await savedObjectsImporter.resolveImportErrors({ readStream: createListStream(toBeSavedObjects), createNewCopies: false, + managed: true, retries, }); diff --git a/x-pack/plugins/fleet/server/services/output.test.ts b/x-pack/plugins/fleet/server/services/output.test.ts index 31efb22834cdb..12abc7c0bec70 100644 --- a/x-pack/plugins/fleet/server/services/output.test.ts +++ b/x-pack/plugins/fleet/server/services/output.test.ts @@ -916,9 +916,9 @@ describe('Output Service', () => { type: 'elasticsearch', hosts: ['http://test:4343'], auth_type: null, + connection_type: null, broker_timeout: null, - broker_ack_reliability: null, - broker_buffer_size: null, + required_acks: null, client_id: null, compression: null, compression_level: null, @@ -1033,13 +1033,14 @@ describe('Output Service', () => { ca_sha256: null, ca_trusted_fingerprint: null, auth_type: null, + connection_type: null, broker_timeout: null, - broker_ack_reliability: null, - broker_buffer_size: null, + required_acks: null, client_id: null, compression: null, compression_level: null, hash: null, + ssl: null, key: null, partition: null, password: null, @@ -1257,10 +1258,13 @@ describe('Output Service', () => { hosts: ['test:4343'], ca_sha256: null, ca_trusted_fingerprint: null, + password: null, + username: null, + ssl: null, + sasl: null, broker_timeout: 10, - broker_ack_reliability: 'Wait for local commit', - broker_buffer_size: 256, - client_id: 'Elastic Agent', + required_acks: 1, + client_id: 'Elastic', compression: 'gzip', compression_level: 4, partition: 'hash', @@ -1285,11 +1289,14 @@ describe('Output Service', () => { expect(soClient.update).toBeCalledWith(expect.anything(), expect.anything(), { hosts: ['test:4343'], broker_timeout: 10, - broker_ack_reliability: 'Wait for local commit', - broker_buffer_size: 256, + required_acks: 1, ca_sha256: null, ca_trusted_fingerprint: null, - client_id: 'Elastic Agent', + password: null, + username: null, + ssl: null, + sasl: null, + client_id: 'Elastic', compression: 'gzip', compression_level: 4, partition: 'hash', @@ -1310,25 +1317,28 @@ describe('Output Service', () => { await outputService.update(soClient, esClientMock, 'output-test', { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, }); expect(soClient.update).toBeCalledWith(expect.anything(), expect.anything(), { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, ca_sha256: null, ca_trusted_fingerprint: null, - client_id: 'Elastic Agent', + password: null, + username: null, + ssl: null, + sasl: null, + client_id: 'Elastic', compression: 'gzip', compression_level: 4, partition: 'hash', timeout: 30, version: '1.0.0', broker_timeout: 10, - broker_ack_reliability: 'Wait for local commit', - broker_buffer_size: 256, + required_acks: 1, }); expect(mockedAgentPolicyService.update).toBeCalledWith( expect.anything(), @@ -1354,7 +1364,7 @@ describe('Output Service', () => { 'output-test', { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, }, { @@ -1364,19 +1374,22 @@ describe('Output Service', () => { expect(soClient.update).toBeCalledWith(expect.anything(), expect.anything(), { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, ca_sha256: null, ca_trusted_fingerprint: null, - client_id: 'Elastic Agent', + password: null, + username: null, + ssl: null, + sasl: null, + client_id: 'Elastic', compression: 'gzip', compression_level: 4, partition: 'hash', timeout: 30, version: '1.0.0', broker_timeout: 10, - broker_ack_reliability: 'Wait for local commit', - broker_buffer_size: 256, + required_acks: 1, }); expect(mockedAgentPolicyService.update).toBeCalledWith( expect.anything(), @@ -1396,25 +1409,28 @@ describe('Output Service', () => { await outputService.update(soClient, esClientMock, 'output-test', { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, }); expect(soClient.update).toBeCalledWith(expect.anything(), expect.anything(), { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, ca_sha256: null, ca_trusted_fingerprint: null, - client_id: 'Elastic Agent', + password: null, + username: null, + ssl: null, + sasl: null, + client_id: 'Elastic', compression: 'gzip', compression_level: 4, partition: 'hash', timeout: 30, version: '1.0.0', broker_timeout: 10, - broker_ack_reliability: 'Wait for local commit', - broker_buffer_size: 256, + required_acks: 1, }); expect(mockedAgentPolicyService.update).toBeCalledWith( expect.anything(), @@ -1438,7 +1454,7 @@ describe('Output Service', () => { 'output-test', { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, }, { @@ -1448,19 +1464,22 @@ describe('Output Service', () => { expect(soClient.update).toBeCalledWith(expect.anything(), expect.anything(), { type: 'kafka', - hosts: ['http://test:4343'], + hosts: ['test:4343'], is_default: true, ca_sha256: null, ca_trusted_fingerprint: null, - client_id: 'Elastic Agent', + password: null, + username: null, + ssl: null, + sasl: null, + client_id: 'Elastic', compression: 'gzip', compression_level: 4, partition: 'hash', timeout: 30, version: '1.0.0', broker_timeout: 10, - broker_ack_reliability: 'Wait for local commit', - broker_buffer_size: 256, + required_acks: 1, }); expect(mockedAgentPolicyService.update).toBeCalledWith( expect.anything(), diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index d95771eb48238..2cf1764a78f59 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -493,7 +493,7 @@ class OutputService { data.compression_level = 4; } if (!output.client_id) { - data.client_id = 'Elastic Agent'; + data.client_id = 'Elastic'; } if (output.username && output.password && !output.sasl?.mechanism) { data.sasl = { @@ -519,11 +519,9 @@ class OutputService { if (!output.broker_timeout) { data.broker_timeout = 10; } - if (!output.broker_ack_reliability) { - data.broker_ack_reliability = kafkaAcknowledgeReliabilityLevel.Commit; - } - if (!output.broker_buffer_size) { - data.broker_buffer_size = 256; + if (output.required_acks === null || output.required_acks === undefined) { + // required_acks can be 0 + data.required_acks = kafkaAcknowledgeReliabilityLevel.Commit; } } @@ -712,6 +710,7 @@ class OutputService { target.key = null; target.compression = null; target.compression_level = null; + target.connection_type = null; target.client_id = null; target.auth_type = null; target.username = null; @@ -725,8 +724,8 @@ class OutputService { target.headers = null; target.timeout = null; target.broker_timeout = null; - target.broker_ack_reliability = null; - target.broker_buffer_size = null; + target.required_acks = null; + target.ssl = null; }; // If the output type changed @@ -766,7 +765,7 @@ class OutputService { updateData.compression_level = 4; } if (!data.client_id) { - updateData.client_id = 'Elastic Agent'; + updateData.client_id = 'Elastic'; } if (data.username && data.password && !data.sasl?.mechanism) { updateData.sasl = { @@ -792,11 +791,9 @@ class OutputService { if (!data.broker_timeout) { updateData.broker_timeout = 10; } - if (!data.broker_ack_reliability) { - updateData.broker_ack_reliability = kafkaAcknowledgeReliabilityLevel.Commit; - } - if (!data.broker_buffer_size) { - updateData.broker_buffer_size = 256; + if (updateData.required_acks === null || updateData.required_acks === undefined) { + // required_acks can be 0 + updateData.required_acks = kafkaAcknowledgeReliabilityLevel.Commit; } } } @@ -808,6 +805,21 @@ class OutputService { updateData.ssl = null; } + if (data.type === outputType.Kafka && updateData.type === outputType.Kafka) { + if (!data.password) { + updateData.password = null; + } + if (!data.username) { + updateData.username = null; + } + if (!data.ssl) { + updateData.ssl = null; + } + if (!data.sasl) { + updateData.sasl = null; + } + } + // ensure only default output exists if (data.is_default) { if (defaultDataOutputId && defaultDataOutputId !== id) { diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index c17f8f4ec2ea3..72fddd0e5b674 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -755,7 +755,6 @@ class PackagePolicyClientImpl implements PackagePolicyClient { packageInfo: pkgInfo, esClient, }); - restOfPackagePolicy = secretsRes.packagePolicyUpdate; secretReferences = secretsRes.secretReferences; secretsToDelete = secretsRes.secretsToDelete; diff --git a/x-pack/plugins/fleet/server/services/secrets.ts b/x-pack/plugins/fleet/server/services/secrets.ts index bd70e8435b02a..7e6efde6c11b0 100644 --- a/x-pack/plugins/fleet/server/services/secrets.ts +++ b/x-pack/plugins/fleet/server/services/secrets.ts @@ -6,19 +6,13 @@ */ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; -import type { BulkResponse } from '@elastic/elasticsearch/lib/api/types'; -import { keyBy, partition } from 'lodash'; +import { keyBy } from 'lodash'; import { set } from '@kbn/safer-lodash-set'; import { packageHasNoPolicyTemplates } from '../../common/services/policy_template'; -import type { - NewPackagePolicy, - PackagePolicyConfigRecordEntry, - RegistryStream, - UpdatePackagePolicy, -} from '../../common'; +import type { NewPackagePolicy, RegistryStream, UpdatePackagePolicy } from '../../common'; import { SO_SEARCH_LIMIT } from '../../common'; import { @@ -34,75 +28,61 @@ import type { Secret, VarSecretReference, PolicySecretReference, + SecretPath, + DeletedSecretResponse, + DeletedSecretReference, } from '../types'; import { FleetError } from '../errors'; -import { SECRETS_INDEX } from '../constants'; +import { SECRETS_ENDPOINT_PATH } from '../constants'; + +import { retryTransientEsErrors } from './epm/elasticsearch/retry'; import { auditLoggingService } from './audit_logging'; import { appContextService } from './app_context'; import { packagePolicyService } from './package_policy'; -interface SecretPath { - path: string; - value: PackagePolicyConfigRecordEntry; -} - -// This will be removed once the secrets index PR is merged into elasticsearch -function getSecretsIndex() { - const testIndex = appContextService.getConfig()?.developer?.testSecretsIndex; - if (testIndex) { - return testIndex; - } - return SECRETS_INDEX; -} - export async function createSecrets(opts: { esClient: ElasticsearchClient; values: string[]; }): Promise { const { esClient, values } = opts; const logger = appContextService.getLogger(); - const body = values.flatMap((value) => [ - { - create: { _index: getSecretsIndex() }, - }, - { value }, - ]); - let res: BulkResponse; - try { - res = await esClient.bulk({ - body, - }); - const [errorItems, successItems] = partition(res.items, (a) => a.create?.error); + const secretsResponse: Secret[] = await Promise.all( + values.map(async (value) => { + try { + return await retryTransientEsErrors( + () => + esClient.transport.request({ + method: 'POST', + path: SECRETS_ENDPOINT_PATH, + body: { value }, + }), + { logger } + ); + } catch (err) { + const msg = `Error creating secrets: ${err}`; + logger.error(msg); + throw new FleetError(msg); + } + }) + ); - successItems.forEach((item) => { - auditLoggingService.writeCustomAuditLog({ - message: `secret created: ${item.create!._id}`, - event: { - action: 'secret_create', - category: ['database'], - type: ['access'], - outcome: 'success', - }, - }); + secretsResponse.forEach((item) => { + auditLoggingService.writeCustomAuditLog({ + message: `secret created: ${item.id}`, + event: { + action: 'secret_create', + category: ['database'], + type: ['access'], + outcome: 'success', + }, }); + }); - if (errorItems.length) { - throw new Error(JSON.stringify(errorItems)); - } - - return res.items.map((item, i) => ({ - id: item.create!._id as string, - value: values[i], - })); - } catch (e) { - const msg = `Error creating secrets in ${getSecretsIndex()} index: ${e}`; - logger.error(msg); - throw new FleetError(msg); - } + return secretsResponse; } export async function deleteSecretsIfNotReferenced(opts: { @@ -190,24 +170,32 @@ export async function _deleteSecrets(opts: { }): Promise { const { esClient, ids } = opts; const logger = appContextService.getLogger(); - const body = ids.flatMap((id) => [ - { - delete: { _index: getSecretsIndex(), _id: id }, - }, - ]); - - let res: BulkResponse; - - try { - res = await esClient.bulk({ - body, - }); - const [errorItems, successItems] = partition(res.items, (a) => a.delete?.error); + const deletedRes: DeletedSecretReference[] = await Promise.all( + ids.map(async (id) => { + try { + const getDeleteRes: DeletedSecretResponse = await retryTransientEsErrors( + () => + esClient.transport.request({ + method: 'DELETE', + path: `${SECRETS_ENDPOINT_PATH}/${id}`, + }), + { logger } + ); + + return { ...getDeleteRes, id }; + } catch (err) { + const msg = `Error deleting secrets: ${err}`; + logger.error(msg); + throw new FleetError(msg); + } + }) + ); - successItems.forEach((item) => { + deletedRes.forEach((item) => { + if (item.deleted === true) { auditLoggingService.writeCustomAuditLog({ - message: `secret deleted: ${item.delete!._id}`, + message: `secret deleted: ${item.id}`, event: { action: 'secret_delete', category: ['database'], @@ -215,16 +203,8 @@ export async function _deleteSecrets(opts: { outcome: 'success', }, }); - }); - - if (errorItems.length) { - throw new Error(JSON.stringify(errorItems)); } - } catch (e) { - const msg = `Error deleting secrets from ${getSecretsIndex()} index: ${e}`; - logger.error(msg); - throw new FleetError(msg); - } + }); } export async function extractAndWriteSecrets(opts: { diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index ec1bde6292770..b9dc7528651eb 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -85,8 +85,11 @@ export type { ExperimentalDataStreamFeature, Secret, SecretElasticDoc, + SecretPath, VarSecretReference, PolicySecretReference, + DeletedSecretResponse, + DeletedSecretReference, PackageListItem, PackageList, InstallationInfo, diff --git a/x-pack/plugins/fleet/server/types/models/output.test.ts b/x-pack/plugins/fleet/server/types/models/output.test.ts index 4441630653a99..06edd900fec2a 100644 --- a/x-pack/plugins/fleet/server/types/models/output.test.ts +++ b/x-pack/plugins/fleet/server/types/models/output.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { validateLogstashHost } from './output'; +import { validateKafkaHost, validateLogstashHost } from './output'; describe('Output model', () => { describe('validateLogstashHost', () => { @@ -23,4 +23,22 @@ describe('Output model', () => { ); }); }); + + describe('validateKafkaHost', () => { + it('should support valid host', () => { + expect(validateKafkaHost('test.fr:5044')).toBeUndefined(); + }); + + it('should return an error for an invalid host', () => { + expect(validateKafkaHost('!@#%&!#!@')).toBe( + 'Invalid format. Expected "host:port" without protocol' + ); + }); + + it('should return an error for an invalid host with http scheme', () => { + expect(validateKafkaHost('https://test.fr:5044')).toBe( + 'Invalid format. Expected "host:port" without protocol' + ); + }); + }); }); diff --git a/x-pack/plugins/fleet/server/types/models/output.ts b/x-pack/plugins/fleet/server/types/models/output.ts index b18d2baa25103..845361116d732 100644 --- a/x-pack/plugins/fleet/server/types/models/output.ts +++ b/x-pack/plugins/fleet/server/types/models/output.ts @@ -8,12 +8,13 @@ import { schema } from '@kbn/config-schema'; import { - kafkaAcknowledgeReliabilityLevel, kafkaAuthType, kafkaCompressionType, + kafkaConnectionType, kafkaPartitionType, kafkaSaslMechanism, kafkaTopicWhenType, + kafkaVerificationModes, outputType, } from '../../../common/constants'; @@ -33,6 +34,21 @@ export function validateLogstashHost(val: string) { } } +export const validateKafkaHost = (input: string): string | undefined => { + const parts = input.split(':'); + + if (parts.length !== 2 || !parts[0] || parts[0].includes('://')) { + return 'Invalid format. Expected "host:port" without protocol'; + } + + const port = parseInt(parts[1], 10); + if (isNaN(port) || port < 1 || port > 65535) { + return 'Invalid port number. Expected a number between 1 and 65535'; + } + + return undefined; +}; + /** * Base schemas */ @@ -50,6 +66,14 @@ const BaseSchema = { certificate_authorities: schema.maybe(schema.arrayOf(schema.string())), certificate: schema.maybe(schema.string()), key: schema.maybe(schema.string()), + verification_mode: schema.maybe( + schema.oneOf([ + schema.literal(kafkaVerificationModes.Full), + schema.literal(kafkaVerificationModes.None), + schema.literal(kafkaVerificationModes.Certificate), + schema.literal(kafkaVerificationModes.Strict), + ]) + ), }) ), proxy_id: schema.nullable(schema.string()), @@ -121,15 +145,9 @@ const KafkaTopicsSchema = schema.arrayOf( schema.object({ type: schema.maybe( schema.oneOf([ - schema.literal(kafkaTopicWhenType.And), - schema.literal(kafkaTopicWhenType.Not), - schema.literal(kafkaTopicWhenType.Or), schema.literal(kafkaTopicWhenType.Equals), schema.literal(kafkaTopicWhenType.Contains), schema.literal(kafkaTopicWhenType.Regexp), - schema.literal(kafkaTopicWhenType.HasFields), - schema.literal(kafkaTopicWhenType.Network), - schema.literal(kafkaTopicWhenType.Range), ]) ), condition: schema.maybe(schema.string()), @@ -142,7 +160,7 @@ const KafkaTopicsSchema = schema.arrayOf( export const KafkaSchema = { ...BaseSchema, type: schema.literal(outputType.Kafka), - hosts: schema.arrayOf(schema.uri({ scheme: ['http', 'https'] }), { minSize: 1 }), + hosts: schema.arrayOf(schema.string({ validate: validateKafkaHost }), { minSize: 1 }), version: schema.maybe(schema.string()), key: schema.maybe(schema.string()), compression: schema.maybe( @@ -161,10 +179,20 @@ export const KafkaSchema = { ), client_id: schema.maybe(schema.string()), auth_type: schema.oneOf([ + schema.literal(kafkaAuthType.None), schema.literal(kafkaAuthType.Userpass), schema.literal(kafkaAuthType.Ssl), schema.literal(kafkaAuthType.Kerberos), ]), + connection_type: schema.conditional( + schema.siblingRef('auth_type'), + kafkaAuthType.None, + schema.oneOf([ + schema.literal(kafkaConnectionType.Plaintext), + schema.literal(kafkaConnectionType.Encryption), + ]), + schema.never() + ), username: schema.conditional( schema.siblingRef('auth_type'), kafkaAuthType.Userpass, @@ -206,13 +234,8 @@ export const KafkaSchema = { ), timeout: schema.maybe(schema.number()), broker_timeout: schema.maybe(schema.number()), - broker_buffer_size: schema.maybe(schema.number()), - broker_ack_reliability: schema.maybe( - schema.oneOf([ - schema.literal(kafkaAcknowledgeReliabilityLevel.Commit), - schema.literal(kafkaAcknowledgeReliabilityLevel.Replica), - schema.literal(kafkaAcknowledgeReliabilityLevel.DoNotWait), - ]) + required_acks: schema.maybe( + schema.oneOf([schema.literal(1), schema.literal(0), schema.literal(-1)]) ), }; @@ -220,9 +243,12 @@ const KafkaUpdateSchema = { ...UpdateSchema, ...KafkaSchema, type: schema.maybe(schema.literal(outputType.Kafka)), - hosts: schema.maybe(schema.arrayOf(schema.uri({ scheme: ['http', 'https'] }), { minSize: 1 })), + hosts: schema.maybe( + schema.arrayOf(schema.string({ validate: validateKafkaHost }), { minSize: 1 }) + ), auth_type: schema.maybe( schema.oneOf([ + schema.literal(kafkaAuthType.None), schema.literal(kafkaAuthType.Userpass), schema.literal(kafkaAuthType.Ssl), schema.literal(kafkaAuthType.Kerberos), diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index 13ccff751c38d..941f27cde7c4d 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -14,6 +14,7 @@ import type { OutputType, ShipperOutput, KafkaAcknowledgeReliabilityLevel, + KafkaConnectionTypeType, } from '../../common/types'; import type { AgentType, FleetServerAgentComponent } from '../../common/types/models'; @@ -159,6 +160,7 @@ export interface OutputSoKafkaAttributes extends OutputSoBaseAttributes { compression?: ValueOf; compression_level?: number; auth_type?: ValueOf; + connection_type?: ValueOf; username?: string; password?: string; sasl?: { @@ -188,8 +190,7 @@ export interface OutputSoKafkaAttributes extends OutputSoBaseAttributes { }>; timeout?: number; broker_timeout?: number; - broker_buffer_size?: number; - broker_ack_reliability?: ValueOf; + required_acks?: ValueOf; } export type OutputSOAttributes = diff --git a/x-pack/plugins/graph/public/application.tsx b/x-pack/plugins/graph/public/application.tsx index 71ff604e1f476..89ba72b203413 100644 --- a/x-pack/plugins/graph/public/application.tsx +++ b/x-pack/plugins/graph/public/application.tsx @@ -33,7 +33,7 @@ import './index.scss'; import { SpacesApi } from '@kbn/spaces-plugin/public'; import { KibanaThemeProvider, toMountPoint } from '@kbn/kibana-react-plugin/public'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; -import { ContentClient } from '@kbn/content-management-plugin/public'; +import { ContentClient, ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { GraphSavePolicy } from './types'; import { graphRouter } from './router'; import { checkLicense } from '../common/check_license'; @@ -71,6 +71,7 @@ export interface GraphDependencies { spaces?: SpacesApi; inspect: InspectorPublicPluginStart; savedObjectsManagement: SavedObjectsManagementPluginStart; + contentManagement: ContentManagementPublicStart; } export type GraphServices = Omit; diff --git a/x-pack/plugins/graph/public/apps/workspace_route.tsx b/x-pack/plugins/graph/public/apps/workspace_route.tsx index b8ca873a1aa33..192ab8daf065a 100644 --- a/x-pack/plugins/graph/public/apps/workspace_route.tsx +++ b/x-pack/plugins/graph/public/apps/workspace_route.tsx @@ -44,6 +44,7 @@ export const WorkspaceRoute = ({ indexPatterns: getIndexPatternProvider, inspect, savedObjectsManagement, + contentManagement, }, }: WorkspaceRouteProps) => { /** @@ -72,9 +73,10 @@ export const WorkspaceRoute = ({ data, unifiedSearch, savedObjectsManagement, + contentManagement, ...coreStart, }), - [coreStart, data, storage, unifiedSearch, savedObjectsManagement] + [coreStart, data, storage, unifiedSearch, savedObjectsManagement, contentManagement] ); const { loading, requestAdapter, callNodeProxy, callSearchNodeProxy, handleSearchQueryError } = diff --git a/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx b/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx index 6b6c06dbc02ba..898ca390499fb 100644 --- a/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx +++ b/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx @@ -22,6 +22,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { connect } from 'react-redux'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { IUnifiedSearchPluginServices } from '@kbn/unified-search-plugin/public/types'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { GraphState, hasDatasourceSelector, @@ -75,9 +76,11 @@ function GuidancePanelComponent(props: GuidancePanelProps) { const { onFillWorkspace, onOpenFieldPicker, onIndexPatternSelected, hasDatasource, hasFields } = props; - const kibana = useKibana(); + const kibana = useKibana< + IUnifiedSearchPluginServices & { contentManagement: ContentManagementPublicStart } + >(); const { services, overlays } = kibana; - const { http, uiSettings, application, data, savedObjectsManagement } = services; + const { application, data, contentManagement, uiSettings } = services; const [hasDataViews, setHasDataViews] = useState(true); useEffect(() => { @@ -90,7 +93,7 @@ function GuidancePanelComponent(props: GuidancePanelProps) { if (!overlays || !application) return null; const onOpenDatasourcePicker = () => { - openSourceModal({ overlays, http, uiSettings, savedObjectsManagement }, onIndexPatternSelected); + openSourceModal({ overlays, contentManagement, uiSettings }, onIndexPatternSelected); }; let content = ( diff --git a/x-pack/plugins/graph/public/components/search_bar.tsx b/x-pack/plugins/graph/public/components/search_bar.tsx index 5bf23c1705dec..101a2b3170f0b 100644 --- a/x-pack/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.tsx @@ -15,6 +15,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { IUnifiedSearchPluginServices } from '@kbn/unified-search-plugin/public/types'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { IndexPatternSavedObject, IndexPatternProvider, WorkspaceField } from '../types'; import { openSourceModal } from '../services/source_modal'; import { @@ -95,7 +96,9 @@ export function SearchBarComponent(props: SearchBarStateProps & SearchBarProps) fetchPattern(); }, [currentDatasource, indexPatternProvider, onIndexPatternChange]); - const kibana = useKibana(); + const kibana = useKibana< + IUnifiedSearchPluginServices & { contentManagement: ContentManagementPublicStart } + >(); const { services, overlays } = kibana; const { uiSettings, @@ -107,7 +110,7 @@ export function SearchBarComponent(props: SearchBarStateProps & SearchBarProps) notifications, http, docLinks, - savedObjectsManagement, + contentManagement, } = services; if (!overlays) return null; return ( @@ -133,7 +136,7 @@ export function SearchBarComponent(props: SearchBarStateProps & SearchBarProps) confirmWipeWorkspace( () => openSourceModal( - { overlays, http, uiSettings, savedObjectsManagement }, + { overlays, contentManagement, uiSettings }, onIndexPatternSelected ), i18n.translate('xpack.graph.clearWorkspace.confirmText', { diff --git a/x-pack/plugins/graph/public/components/source_picker.tsx b/x-pack/plugins/graph/public/components/source_picker.tsx index ec227fdddc5bd..1c63c359ef938 100644 --- a/x-pack/plugins/graph/public/components/source_picker.tsx +++ b/x-pack/plugins/graph/public/components/source_picker.tsx @@ -8,29 +8,28 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { CoreStart } from '@kbn/core/public'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { IndexPatternSavedObject } from '../types'; export interface SourcePickerProps { onIndexPatternSelected: (indexPattern: IndexPatternSavedObject) => void; - http: CoreStart['http']; - uiSettings: CoreStart['uiSettings']; - savedObjectsManagement: SavedObjectsManagementPluginStart; + contentManagement: ContentManagementPublicStart; + uiSettings: IUiSettingsClient; } const fixedPageSize = 8; export function SourcePicker({ - http, - uiSettings, - savedObjectsManagement, + contentManagement, onIndexPatternSelected, + uiSettings, }: SourcePickerProps) { return ( { onIndexPatternSelected(indexPattern as IndexPatternSavedObject); }} @@ -45,9 +44,9 @@ export function SourcePicker({ name: i18n.translate('xpack.graph.sourceModal.savedObjectType.dataView', { defaultMessage: 'Data view', }), - showSavedObject: (indexPattern) => !indexPattern.attributes.type, + showSavedObject: (indexPattern: SavedObjectCommon<{ type?: string; title: string }>) => + !indexPattern.attributes.type, includeFields: ['type'], - defaultSearchField: 'name', }, ]} fixedPageSize={fixedPageSize} diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index 73e040dc4e760..3fc9b72873297 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -132,6 +132,7 @@ export class GraphPlugin spaces: pluginsStart.spaces, inspect: pluginsStart.inspector, savedObjectsManagement: pluginsStart.savedObjectsManagement, + contentManagement: pluginsStart.contentManagement, }); }, }); diff --git a/x-pack/plugins/graph/public/services/source_modal.tsx b/x-pack/plugins/graph/public/services/source_modal.tsx index 6da003833d1a9..791471b8a2470 100644 --- a/x-pack/plugins/graph/public/services/source_modal.tsx +++ b/x-pack/plugins/graph/public/services/source_modal.tsx @@ -5,32 +5,29 @@ * 2.0. */ -import { CoreStart } from '@kbn/core/public'; import React from 'react'; import { KibanaReactOverlays } from '@kbn/kibana-react-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SourceModal } from '../components/source_modal'; import { IndexPatternSavedObject } from '../types'; export function openSourceModal( { overlays, - http, + contentManagement, uiSettings, - savedObjectsManagement, }: { overlays: KibanaReactOverlays; - http: CoreStart['http']; - uiSettings: CoreStart['uiSettings']; - savedObjectsManagement: SavedObjectsManagementPluginStart; + contentManagement: ContentManagementPublicStart; + uiSettings: IUiSettingsClient; }, onSelected: (indexPattern: IndexPatternSavedObject) => void ) { const modalRef = overlays.openModal( { onSelected(indexPattern); modalRef.close(); diff --git a/x-pack/plugins/graph/tsconfig.json b/x-pack/plugins/graph/tsconfig.json index 4ee1639a2d4f8..f4dc6a3faaf73 100644 --- a/x-pack/plugins/graph/tsconfig.json +++ b/x-pack/plugins/graph/tsconfig.json @@ -45,6 +45,7 @@ "@kbn/object-versioning", "@kbn/content-management-table-list-view-table", "@kbn/content-management-table-list-view", + "@kbn/core-ui-settings-browser", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/index_management/public/application/mount_management_section.ts b/x-pack/plugins/index_management/public/application/mount_management_section.ts index d00aa6ff1f0e6..6bb3b834ce85f 100644 --- a/x-pack/plugins/index_management/public/application/mount_management_section.ts +++ b/x-pack/plugins/index_management/public/application/mount_management_section.ts @@ -53,7 +53,7 @@ export async function mountManagementSection( extensionsService: ExtensionsService, isFleetEnabled: boolean, kibanaVersion: SemVer, - enableIndexActions: boolean + enableIndexActions: boolean = true ) { const { element, setBreadcrumbs, history, theme$ } = params; const [core, startDependencies] = await coreSetup.getStartServices(); diff --git a/x-pack/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts index 59954c6659494..20d2405a0fa4b 100644 --- a/x-pack/plugins/index_management/public/types.ts +++ b/x-pack/plugins/index_management/public/types.ts @@ -28,5 +28,5 @@ export interface ClientConfigType { ui: { enabled: boolean; }; - enableIndexActions: boolean; + enableIndexActions?: boolean; } diff --git a/x-pack/plugins/index_management/server/config.ts b/x-pack/plugins/index_management/server/config.ts index 4fd24bf3fcdf7..c5d459486a8ef 100644 --- a/x-pack/plugins/index_management/server/config.ts +++ b/x-pack/plugins/index_management/server/config.ts @@ -22,7 +22,14 @@ const schemaLatest = schema.object( ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), - enableIndexActions: schema.boolean({ defaultValue: true }), + enableIndexActions: schema.conditional( + schema.contextRef('serverless'), + true, + // Index actions are disabled in serverless; refer to the serverless.yml file as the source of truth + // We take this approach in order to have a central place (serverless.yml) for serverless config across Kibana + schema.boolean({ defaultValue: true }), + schema.never() + ), }, { defaultValue: undefined } ); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx index 66d26dc836cf1..be1a753edb5fa 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx @@ -14,11 +14,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { DataView } from '@kbn/data-views-plugin/common'; import { - LogRateAnalysisContent, LOG_RATE_ANALYSIS_TYPE, - type LogRateAnalysisResultsData, type LogRateAnalysisType, -} from '@kbn/aiops-plugin/public'; +} from '@kbn/aiops-utils/log_rate_analysis_type'; +import { LogRateAnalysisContent, type LogRateAnalysisResultsData } from '@kbn/aiops-plugin/public'; import { Rule } from '@kbn/alerting-plugin/common'; import { TopAlert } from '@kbn/observability-plugin/public'; import { @@ -33,7 +32,6 @@ import { ALERT_END } from '@kbn/rule-data-utils'; import { Color, colorTransformer } from '../../../../../../common/color_palette'; import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; import { - Comparator, CountRuleParams, isRatioRuleParams, PartialRuleParams, @@ -60,11 +58,9 @@ export const LogRateAnalysis: FC = ({ r const [dataView, setDataView] = useState(); const [esSearchQuery, setEsSearchQuery] = useState(); const [logRateAnalysisParams, setLogRateAnalysisParams] = useState< - { significantFieldValues: SignificantFieldValue[] } | undefined + | { logRateAnalysisType: LogRateAnalysisType; significantFieldValues: SignificantFieldValue[] } + | undefined >(); - const [logRateAnalysisType, setLogRateAnalysisType] = useState( - undefined - ); const validatedParams = useMemo(() => decodeOrThrow(ruleParamsRT)(rule.params), [rule]); @@ -95,19 +91,6 @@ export const LogRateAnalysis: FC = ({ r if (!isRatioRuleParams(validatedParams)) { getDataView(); - - switch (validatedParams.count.comparator) { - case Comparator.GT: - case Comparator.GT_OR_EQ: - setLogRateAnalysisType(LOG_RATE_ANALYSIS_TYPE.SPIKE); - break; - case Comparator.LT: - case Comparator.LT_OR_EQ: - setLogRateAnalysisType(LOG_RATE_ANALYSIS_TYPE.DIP); - break; - default: - setLogRateAnalysisType(undefined); - } } }, [validatedParams, alert, dataViews, logsShared]); @@ -188,7 +171,13 @@ export const LogRateAnalysis: FC = ({ r ['pValue', 'docCount'], ['asc', 'asc'] ).slice(0, 50); - setLogRateAnalysisParams(significantFieldValues ? { significantFieldValues } : undefined); + + const logRateAnalysisType = analysisResults?.analysisType; + setLogRateAnalysisParams( + significantFieldValues && logRateAnalysisType + ? { logRateAnalysisType, significantFieldValues } + : undefined + ); }; const aiAssistant = useObservabilityAIAssistant(); @@ -201,6 +190,8 @@ export const LogRateAnalysis: FC = ({ r return undefined; } + const { logRateAnalysisType } = logRateAnalysisParams; + const header = 'Field name,Field value,Doc count,p-value'; const rows = logRateAnalysisParams.significantFieldValues .map((item) => Object.values(item).join(',')) @@ -210,27 +201,34 @@ export const LogRateAnalysis: FC = ({ r "Log Rate Analysis" is an AIOps feature that uses advanced statistical methods to identify reasons for increases and decreases in log rates. It makes it easy to find and investigate causes of unusual spikes or dips by using the analysis workflow view. You are using "Log Rate Analysis" and ran the statistical analysis on the log messages which occured during the alert. You received the following analysis results from "Log Rate Analysis" which list statistically significant co-occuring field/value combinations sorted from most significant (lower p-values) to least significant (higher p-values) that ${ - logRateAnalysisType === 'spike' + logRateAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE ? 'contribute to the log rate spike' : 'are less or not present in the log rate dip' }: + ${ + logRateAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE + ? 'The median log rate in the selected deviation time range is higher than the baseline. Therefore, the results shows statistically significant items within the deviation time range that are contributors to the spike. The "doc count" column refers to the amount of documents in the deviation time range.' + : 'The median log rate in the selected deviation time range is lower than the baseline. Therefore, the analysis results table shows statistically significant items within the baseline time range that are less in number or missing within the deviation time range. The "doc count" column refers to the amount of documents in the baseline time range.' + } + ${header} ${rows} Based on the above analysis results and your observability expert knowledge, output the following: Analyse the type of these logs and explain their usual purpose (1 paragraph). ${ - logRateAnalysisType === 'spike' + logRateAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE ? 'Based on the type of these logs do a root cause analysis on why the field and value combinations from the analysis results are causing this log rate spike (2 parapraphs)' - : 'Based on the type of these logs do a concise analysis why the statistically significant field and value combinations are less present or missing from the log rate dip with concrete examples based on the analysis results data. Do not guess, just output what you are sure of (2 paragraphs)' + : 'Based on the type of these logs explain why the statistically significant field and value combinations are less in number or missing from the log rate dip with concrete examples based on the analysis results data which contains items that are present in the baseline time range and are missing or less in number in the deviation time range (2 paragraphs)' }. ${ - logRateAnalysisType === 'spike' + logRateAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE ? 'Recommend concrete remediations to resolve the root cause (3 bullet points).' : '' } - Do not repeat the given instructions in your output.`; + + Do not mention indidivual p-values from the analysis results. Do not guess, just say what you are sure of. Do not repeat the given instructions in your output.`; const now = new Date().toString(); @@ -251,7 +249,7 @@ export const LogRateAnalysis: FC = ({ r }, }, ]; - }, [logRateAnalysisParams, logRateAnalysisType]); + }, [logRateAnalysisParams]); if (!dataView || !esSearchQuery) return null; @@ -271,7 +269,6 @@ export const LogRateAnalysis: FC = ({ r { +import type { FormulaConfig } from '../../../types'; +import type { MetricLayerOptions } from '../../visualization_types'; + +export const KPI_CHART_HEIGHT = 150; +export const AVERAGE_SUBTITLE = i18n.translate( + 'xpack.infra.assetDetailsEmbeddable.overview.kpi.subtitle.average', + { + defaultMessage: 'Average', + } +); + +export interface KPIChartProps extends Pick { layers: Layer; toolTip: string; } @@ -22,7 +30,7 @@ export interface KPIChartProps export const KPI_CHARTS: KPIChartProps[] = [ { id: 'cpuUsage', - title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpuUsage.title', { + title: i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.kpi.cpuUsage.title', { defaultMessage: 'CPU Usage', }), layers: { @@ -45,9 +53,12 @@ export const KPI_CHARTS: KPIChartProps[] = [ }, { id: 'normalizedLoad1m', - title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.title', { - defaultMessage: 'CPU Usage', - }), + title: i18n.translate( + 'xpack.infra.assetDetailsEmbeddable.overview.kpi.normalizedLoad1m.title', + { + defaultMessage: 'CPU Usage', + } + ), layers: { data: { ...hostLensFormulas.normalizedLoad1m, @@ -68,7 +79,7 @@ export const KPI_CHARTS: KPIChartProps[] = [ }, { id: 'memoryUsage', - title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memoryUsage.title', { + title: i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.kpi.memoryUsage.title', { defaultMessage: 'CPU Usage', }), layers: { @@ -91,7 +102,7 @@ export const KPI_CHARTS: KPIChartProps[] = [ }, { id: 'diskSpaceUsage', - title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.diskSpaceUsage.title', { + title: i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.kpi.diskSpaceUsage.title', { defaultMessage: 'CPU Usage', }), layers: { diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts index 946e26cec62a1..48fa795e9688a 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts @@ -9,7 +9,7 @@ import type { FormulaConfig } from '../../../types'; export const diskReadThroughput: FormulaConfig = { label: 'Disk Read Throughput', - value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')", + value: "counter_rate(max(system.diskio.read.bytes), kql='system.diskio.read.bytes: *')", format: { id: 'bytes', params: { diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_availability.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_availability.ts new file mode 100644 index 0000000000000..aadbd8ccea650 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_availability.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaConfig } from '../../../types'; + +export const diskSpaceAvailability: FormulaConfig = { + label: 'Disk Space Availability', + value: '1 - average(system.filesystem.used.pct)', + format: { + id: 'percent', + params: { + decimals: 0, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts index e391bce4a1151..aed685aa34d8c 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts @@ -9,7 +9,7 @@ import type { FormulaConfig } from '../../../types'; export const diskWriteThroughput: FormulaConfig = { label: 'Disk Write Throughput', - value: "counter_rate(max(system.diskio.write.count), kql='system.diskio.write.count: *')", + value: "counter_rate(max(system.diskio.write.bytes), kql='system.diskio.write.bytes: *')", format: { id: 'bytes', params: { diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts index 6ac608a97d2e4..9cec01155ac19 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts @@ -10,6 +10,7 @@ export { diskIORead } from './disk_read_iops'; export { diskIOWrite } from './disk_write_iops'; export { diskReadThroughput } from './disk_read_throughput'; export { diskWriteThroughput } from './disk_write_throughput'; +export { diskSpaceAvailability } from './disk_space_availability'; export { diskSpaceAvailable } from './disk_space_available'; export { diskSpaceUsage } from './disk_space_usage'; export { hostCount } from './host_count'; diff --git a/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_state.ts b/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_state.ts index edcd1d0627d3d..4e88dc368ca0a 100644 --- a/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_state.ts +++ b/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_state.ts @@ -9,7 +9,7 @@ import type { DataViewField, DataView } from '@kbn/data-views-plugin/common'; import { UseAssetDetailsStateProps } from '../../../hooks/use_asset_details_state'; export const assetDetailsState: UseAssetDetailsStateProps['state'] = { - node: { + asset: { name: 'host1', id: 'host1-macOS', ip: '192.168.0.1', @@ -29,7 +29,7 @@ export const assetDetailsState: UseAssetDetailsStateProps['state'] = { showActionsColumn: true, }, }, - nodeType: 'host', + assetType: 'host', dateRange: { from: '2023-04-09T11:07:49Z', to: '2023-04-09T11:23:49Z', diff --git a/x-pack/plugins/infra/public/components/asset_details/asset_details.stories.tsx b/x-pack/plugins/infra/public/components/asset_details/asset_details.stories.tsx index a90fe5764f531..824a4e5f65ef0 100644 --- a/x-pack/plugins/infra/public/components/asset_details/asset_details.stories.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/asset_details.stories.tsx @@ -22,42 +22,36 @@ const tabs: Tab[] = [ name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', { defaultMessage: 'Overview', }), - 'data-test-subj': 'hostsView-flyout-tabs-overview', }, { id: FlyoutTabIds.LOGS, name: i18n.translate('xpack.infra.nodeDetails.tabs.logs', { defaultMessage: 'Logs', }), - 'data-test-subj': 'hostsView-flyout-tabs-logs', }, { id: FlyoutTabIds.METADATA, name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.metadata', { defaultMessage: 'Metadata', }), - 'data-test-subj': 'hostsView-flyout-tabs-metadata', }, { id: FlyoutTabIds.PROCESSES, name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', { defaultMessage: 'Processes', }), - 'data-test-subj': 'hostsView-flyout-tabs-processes', }, { id: FlyoutTabIds.ANOMALIES, name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', { defaultMessage: 'Anomalies', }), - 'data-test-subj': 'hostsView-flyout-tabs-anomalies', }, { id: FlyoutTabIds.LINK_TO_APM, name: i18n.translate('xpack.infra.infra.nodeDetails.apmTabLabel', { defaultMessage: 'APM', }), - 'data-test-subj': 'hostsView-flyout-apm-link', }, ]; @@ -96,7 +90,7 @@ const FlyoutTemplate: Story = (args) => { Open flyout ); @@ -107,7 +101,7 @@ export const Page = PageTemplate.bind({}); export const Flyout = FlyoutTemplate.bind({}); Flyout.args = { renderMode: { - showInFlyout: true, + mode: 'flyout', closeFlyout: () => {}, }, }; diff --git a/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx b/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx index 101a23a4d084e..238a8c5f00250 100644 --- a/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx @@ -7,11 +7,17 @@ import React from 'react'; import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody } from '@elastic/eui'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; import type { AssetDetailsProps, RenderMode } from './types'; import { Content } from './content/content'; import { Header } from './header/header'; -import { TabSwitcherProvider } from './hooks/use_tab_switcher'; -import { AssetDetailsStateProvider } from './hooks/use_asset_details_state'; +import { TabSwitcherProvider, useTabSwitcherContext } from './hooks/use_tab_switcher'; +import { + AssetDetailsStateProvider, + useAssetDetailsStateContext, +} from './hooks/use_asset_details_state'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; +import { ASSET_DETAILS_FLYOUT_COMPONENT_NAME } from './constants'; interface ContentTemplateProps { header: React.ReactElement; @@ -20,8 +26,27 @@ interface ContentTemplateProps { } const ContentTemplate = ({ header, body, renderMode }: ContentTemplateProps) => { - return renderMode.showInFlyout ? ( - + const { assetType } = useAssetDetailsStateContext(); + const { initialActiveTabId } = useTabSwitcherContext(); + const { + services: { telemetry }, + } = useKibanaContextForPlugin(); + + useEffectOnce(() => { + telemetry.reportAssetDetailsFlyoutViewed({ + componentName: ASSET_DETAILS_FLYOUT_COMPONENT_NAME, + assetType, + tabId: initialActiveTabId, + }); + }); + + return renderMode.mode === 'flyout' ? ( + {header} {body} @@ -34,25 +59,27 @@ const ContentTemplate = ({ header, body, renderMode }: ContentTemplateProps) => }; export const AssetDetails = ({ - node, + asset, dateRange, activeTabId, overrides, onTabsStateChange, tabs = [], links = [], - nodeType = 'host', + assetType = 'host', renderMode = { - showInFlyout: false, + mode: 'page', }, }: AssetDetailsProps) => { return ( - + 0 ? activeTabId ?? tabs[0].id : undefined} > } + header={
} body={} renderMode={renderMode} /> diff --git a/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx b/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx index 355d8fac0055b..534ba4c2e4265 100644 --- a/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx @@ -73,8 +73,8 @@ export class AssetDetailsEmbeddable extends Embeddable { values={{ documentation: ( diff --git a/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx b/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx index 60f487d52d0ea..0cd5e1a53013a 100644 --- a/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx @@ -31,7 +31,10 @@ export const ExpandableContent = (props: ExpandableContentProps) => { {shouldShowMore && ( <> {' ... '} - + & { const APM_FIELD = 'host.hostname'; export const Header = ({ tabs = [], links = [], compact }: Props) => { - const { node, nodeType, overrides, dateRange: timeRange } = useAssetDetailsStateContext(); + const { asset, assetType, overrides, dateRange: timeRange } = useAssetDetailsStateContext(); const { euiTheme } = useEuiTheme(); const { showTab, activeTabId } = useTabSwitcherContext(); @@ -46,23 +47,23 @@ export const Header = ({ tabs = [], links = [], compact }: Props) => { const tabLinkComponents = { [FlyoutTabIds.LINK_TO_APM]: (tab: Tab) => ( - + ), [FlyoutTabIds.LINK_TO_UPTIME]: (tab: Tab) => ( - + ), }; const topCornerLinkComponents: Record = { nodeDetails: ( ), alertRule: , - apmServices: , + apmServices: , }; const tabEntries = tabs.map(({ name, ...tab }) => { @@ -77,6 +78,7 @@ export const Header = ({ tabs = [], links = [], compact }: Props) => { return ( onTabClick(tab.id)} isSelected={tab.id === activeTabId} @@ -102,7 +104,7 @@ export const Header = ({ tabs = [], links = [], compact }: Props) => { `} > - {compact ?

{node.name}

:

{node.name}

} + {compact ?

{asset.name}

:

{asset.name}

}
; } export function useAssetDetailsState({ state }: UseAssetDetailsStateProps) { - const { node, nodeType, dateRange: rawDateRange, onTabsStateChange, overrides } = state; + const { asset, assetType, dateRange: rawDateRange, onTabsStateChange, overrides } = state; const dateRange = useMemo(() => { const { from = DEFAULT_DATE_RANGE.from, to = DEFAULT_DATE_RANGE.to } = @@ -36,8 +36,8 @@ export function useAssetDetailsState({ state }: UseAssetDetailsStateProps) { const dateRangeTs = toTimestampRange(dateRange); return { - node, - nodeType, + asset, + assetType, dateRange, dateRangeTs, onTabsStateChange, diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx b/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx index 60dc710b8613f..6bdcbca214d37 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx @@ -33,6 +33,7 @@ export function useTabSwitcher({ initialActiveTabId }: TabSwitcherParams) { }; return { + initialActiveTabId, activeTabId, renderedTabsSet, showTab, diff --git a/x-pack/plugins/infra/public/components/asset_details/links/link_to_alerts.tsx b/x-pack/plugins/infra/public/components/asset_details/links/link_to_alerts.tsx index 47d7075cb60dc..e5a5cc6340abe 100644 --- a/x-pack/plugins/infra/public/components/asset_details/links/link_to_alerts.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/links/link_to_alerts.tsx @@ -15,7 +15,7 @@ export interface LinkToAlertsRuleProps { export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRuleProps) => { return ( { +export const LinkToAlertsPage = ({ assetName, queryField, dateRange }: LinkToAlertsPageProps) => { const { services } = useKibanaContextForPlugin(); const { http } = services; const linkToAlertsPage = http.basePath.prepend( `${ALERTS_PATH}?_a=${encode({ - kuery: `${queryField}:"${nodeName}"`, + kuery: `${queryField}:"${assetName}"`, rangeFrom: dateRange.from, rangeTo: dateRange.to, status: 'all', @@ -35,7 +35,7 @@ export const LinkToAlertsPage = ({ nodeName, queryField, dateRange }: LinkToAler return ( { +export const LinkToApmServices = ({ assetName, apmField }: LinkToApmServicesProps) => { const { services } = useKibanaContextForPlugin(); const { http } = services; const queryString = new URLSearchParams( encode( stringify({ - kuery: `${apmField}:"${nodeName}"`, + kuery: `${apmField}:"${assetName}"`, }) ) ); @@ -34,7 +34,7 @@ export const LinkToApmServices = ({ nodeName, apmField }: LinkToApmServicesProps return ( { - const inventoryModel = findInventoryModel(nodeType); + const inventoryModel = findInventoryModel(assetType); const nodeDetailFrom = currentTimestamp - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000; const nodeDetailMenuItemLinkProps = useLinkProps({ ...getNodeDetailUrl({ - nodeType, - nodeId: nodeName, + nodeType: assetType, + nodeId: assetName, from: nodeDetailFrom, to: currentTimestamp, }), @@ -37,7 +37,7 @@ export const LinkToNodeDetails = ({ return ( { +export const TabToApmTraces = ({ assetName, apmField, name, ...props }: LinkToApmServicesProps) => { const { euiTheme } = useEuiTheme(); const apmTracesMenuItemLinkProps = useLinkProps({ app: 'apm', hash: 'traces', search: { - kuery: `${apmField}:"${nodeName}"`, + kuery: `${apmField}:"${assetName}"`, }, }); @@ -30,7 +30,7 @@ export const TabToApmTraces = ({ nodeName, apmField, name, ...props }: LinkToApm { +export const TabToUptime = ({ + assetType, + assetName, + nodeIp, + name, + ...props +}: LinkToUptimeProps) => { const { share } = useKibanaContextForPlugin().services; const { euiTheme } = useEuiTheme(); return ( share.url.locators .get(uptimeOverviewLocatorID)! - .navigate({ [nodeType]: nodeName, ip: nodeIp }) + .navigate({ [assetType]: assetName, ip: nodeIp }) } > { - const { node, overrides } = useAssetDetailsStateContext(); + const { asset, overrides } = useAssetDetailsStateContext(); const { onClose = () => {} } = overrides?.anomalies ?? {}; - return ; + return ; }; diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx index 087c34551c440..35337032805c1 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx @@ -22,7 +22,7 @@ import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state const TEXT_QUERY_THROTTLE_INTERVAL_MS = 500; export const Logs = () => { - const { node, nodeType, overrides, onTabsStateChange, dateRangeTs } = + const { asset, assetType, overrides, onTabsStateChange, dateRangeTs } = useAssetDetailsStateContext(); const { logView: overrideLogView, query: overrideQuery } = overrides?.logs ?? {}; @@ -49,7 +49,7 @@ export const Logs = () => { const filter = useMemo(() => { const query = [ - `${findInventoryFields(nodeType).id}: "${node.name}"`, + `${findInventoryFields(assetType).id}: "${asset.name}"`, ...(textQueryDebounced !== '' ? [textQueryDebounced] : []), ].join(' and '); @@ -57,7 +57,7 @@ export const Logs = () => { language: 'kuery', query, }; - }, [nodeType, node.name, textQueryDebounced]); + }, [assetType, asset.name, textQueryDebounced]); const onQueryChange = useCallback((e: React.ChangeEvent) => { setTextQuery(e.target.value); @@ -70,13 +70,20 @@ export const Logs = () => { const logsUrl = useMemo(() => { return locators.nodeLogsLocator.getRedirectUrl({ - nodeType, - nodeId: node.name, + nodeType: assetType, + nodeId: asset.name, time: startTimestamp, filter: textQueryDebounced, logView, }); - }, [locators.nodeLogsLocator, node.name, nodeType, startTimestamp, textQueryDebounced, logView]); + }, [ + locators.nodeLogsLocator, + asset.name, + assetType, + startTimestamp, + textQueryDebounced, + logView, + ]); return ( diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx index 31d978235be32..a2d04b1c36184 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx @@ -75,7 +75,7 @@ export const AddMetadataFilterButton = ({ item }: AddMetadataFilterButtonProps) color="text" iconType="filter" display="base" - data-test-subj="hostsView-flyout-metadata-remove-filter" + data-test-subj="infraAssetDetailsMetadataRemoveFilterButton" aria-label={i18n.translate('xpack.infra.metadataEmbeddable.filterAriaLabel', { defaultMessage: 'Filter', })} @@ -102,7 +102,7 @@ export const AddMetadataFilterButton = ({ item }: AddMetadataFilterButtonProps) color="primary" size="s" iconType="filter" - data-test-subj="hostsView-flyout-metadata-add-filter" + data-test-subj="infraAssetDetailsMetadataAddFilterButton" aria-label={i18n.translate('xpack.infra.metadataEmbeddable.AddFilterAriaLabel', { defaultMessage: 'Add Filter', })} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_pin_to_row.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_pin_to_row.tsx index a1e7c3f106497..1e5e31b887911 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_pin_to_row.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_pin_to_row.tsx @@ -47,8 +47,8 @@ export const AddMetadataPinToRow = ({ size="s" color="primary" iconType="pinFilled" - data-test-subj="infraMetadataEmbeddableRemovePin" - aria-label={i18n.translate('xpack.infra.metadataEmbeddable.pinAriaLabel', { + data-test-subj="infraAssetDetailsMetadataRemovePin" + aria-label={i18n.translate('xpack.infra.metadata.pinAriaLabel', { defaultMessage: 'Pinned field', })} onClick={handleRemovePin} @@ -65,7 +65,7 @@ export const AddMetadataPinToRow = ({ color="primary" size="s" iconType="pin" - data-test-subj="infraMetadataEmbeddableAddPin" + data-test-subj="infraAssetDetailsMetadataAddPin" aria-label={PIN_FIELD} onClick={handleAddPin} /> diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.test.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.test.tsx index eeff58d30d1e7..9f8a04ef64e6c 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.test.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.test.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { Metadata } from './metadata'; - import { useMetadata } from '../../hooks/use_metadata'; import { useSourceContext } from '../../../../containers/metrics_source'; import { render } from '@testing-library/react'; @@ -27,8 +26,8 @@ const renderHostMetadata = () => from: '2023-04-09T11:07:49Z', to: '2023-04-09T11:23:49Z', }, - nodeType: 'host', - node: { + assetType: 'host', + asset: { id: 'host-1', name: 'host-1', }, @@ -69,30 +68,30 @@ describe('Single Host Metadata (Hosts View)', () => { mockUseMetadata({ error: 'Internal server error' }); const result = renderHostMetadata(); - expect(result.queryByTestId('infraMetadataErrorCallout')).toBeInTheDocument(); + expect(result.queryByTestId('infraAssetDetailsMetadataErrorCallout')).toBeInTheDocument(); }); it('should show an no data message if fetching the metadata returns an empty array', async () => { mockUseMetadata({ metadata: [] }); const result = renderHostMetadata(); - expect(result.queryByTestId('infraHostMetadataSearchBarInput')).toBeInTheDocument(); - expect(result.queryByTestId('infraHostMetadataNoData')).toBeInTheDocument(); + expect(result.queryByTestId('infraAssetDetailsMetadataSearchBarInput')).toBeInTheDocument(); + expect(result.queryByTestId('infraAssetDetailsMetadataNoData')).toBeInTheDocument(); }); it('should show the metadata table if metadata is returned', async () => { mockUseMetadata({ metadata: [{ name: 'host.os.name', value: 'Ubuntu' }] }); const result = renderHostMetadata(); - expect(result.queryByTestId('infraHostMetadataSearchBarInput')).toBeInTheDocument(); - expect(result.queryByTestId('infraMetadataTable')).toBeInTheDocument(); + expect(result.queryByTestId('infraAssetDetailsMetadataSearchBarInput')).toBeInTheDocument(); + expect(result.queryByTestId('infraAssetDetailsMetadataTable')).toBeInTheDocument(); }); it('should return loading text if loading', async () => { mockUseMetadata({ loading: true }); const result = renderHostMetadata(); - expect(result.queryByTestId('infraHostMetadataSearchBarInput')).toBeInTheDocument(); - expect(result.queryByTestId('infraHostMetadataLoading')).toBeInTheDocument(); + expect(result.queryByTestId('infraAssetDetailsMetadataSearchBarInput')).toBeInTheDocument(); + expect(result.queryByTestId('infraAssetDetailsMetadataLoading')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx index 23a3e5f193409..4a759e5718d13 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx @@ -24,26 +24,26 @@ export interface MetadataSearchUrlState { } export interface MetadataProps { + assetName: string; + assetType: InventoryItemType; dateRange: TimeRange; - nodeName: string; - nodeType: InventoryItemType; showActionsColumn?: boolean; search?: string; onSearchChange?: (query: string) => void; } export const Metadata = () => { - const { node, nodeType, overrides, dateRangeTs, onTabsStateChange } = + const { asset, assetType, overrides, dateRangeTs, onTabsStateChange } = useAssetDetailsStateContext(); const { query, showActionsColumn = false } = overrides?.metadata ?? {}; - const inventoryModel = findInventoryModel(nodeType); + const inventoryModel = findInventoryModel(assetType); const { sourceId } = useSourceContext(); const { loading: metadataLoading, error: fetchMetadataError, metadata, - } = useMetadata(node.name, nodeType, inventoryModel.requiredMetrics, sourceId, dateRangeTs); + } = useMetadata(asset.name, assetType, inventoryModel.requiredMetrics, sourceId, dateRangeTs); const fields = useMemo(() => getAllFields(metadata), [metadata]); @@ -64,7 +64,7 @@ export const Metadata = () => { })} color="danger" iconType="error" - data-test-subj="infraMetadataErrorCallout" + data-test-subj="infraAssetDetailsMetadataErrorCallout" > {LOADING} +
{LOADING}
) : ( -
{NO_METADATA_FOUND}
+
{NO_METADATA_FOUND}
) } /> diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/osquery/osquery.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/osquery/osquery.tsx index 06640540af16d..b18a2f802e085 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/osquery/osquery.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/osquery/osquery.tsx @@ -14,12 +14,12 @@ import { useMetadata } from '../../hooks/use_metadata'; import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state'; export const Osquery = () => { - const { node, nodeType, dateRangeTs } = useAssetDetailsStateContext(); - const inventoryModel = findInventoryModel(nodeType); + const { asset, assetType, dateRangeTs } = useAssetDetailsStateContext(); + const inventoryModel = findInventoryModel(assetType); const { sourceId } = useSourceContext(); const { loading, metadata } = useMetadata( - node.name, - nodeType, + asset.name, + assetType, inventoryModel.requiredMetrics, sourceId, dateRangeTs diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx index 87ebaae1f5f20..2edac4abbbdda 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx @@ -25,12 +25,12 @@ import { useBoolean } from '../../../../hooks/use_boolean'; import { ALERT_STATUS_ALL } from '../../../../common/alerts/constants'; export const AlertsSummaryContent = ({ - nodeName, - nodeType, + assetName, + assetType, dateRange, }: { - nodeName: string; - nodeType: InventoryItemType; + assetName: string; + assetType: InventoryItemType; dateRange: TimeRange; }) => { const [isAlertFlyoutVisible, { toggle: toggleAlertFlyout }] = useBoolean(false); @@ -39,10 +39,10 @@ export const AlertsSummaryContent = ({ () => createAlertsEsQuery({ dateRange, - hostNodeNames: [nodeName], + hostNodeNames: [assetName], status: ALERT_STATUS_ALL, }), - [nodeName, dateRange] + [assetName, dateRange] ); return ( @@ -56,8 +56,8 @@ export const AlertsSummaryContent = ({
@@ -65,8 +65,8 @@ export const AlertsSummaryContent = ({ @@ -112,7 +112,7 @@ const AlertsSectionTitle = () => { return ( - +
{ diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx index b86201a29098c..dfc7f8823ba71 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx @@ -4,21 +4,55 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; + +import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Tile, type TileProps } from './tile'; -import { KPI_CHARTS } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import type { TimeRange } from '@kbn/es-query'; +import { LensChart, TooltipContent } from '../../../../lens'; +import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; +import { + KPI_CHARTS, + KPI_CHART_HEIGHT, + AVERAGE_SUBTITLE, +} from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config'; + +interface Props { + dataView?: DataView; + nodeName: string; + timeRange: TimeRange; +} + +export const KPIGrid = React.memo(({ nodeName, dataView, timeRange }: Props) => { + const filters = useMemo(() => { + return [ + buildCombinedHostsFilter({ + field: 'host.name', + values: [nodeName], + dataView, + }), + ]; + }, [dataView, nodeName]); -export const KPIGrid = React.memo(({ nodeName, dataView, timeRange: dateRange }: TileProps) => { return ( - <> - - {KPI_CHARTS.map((chartProp, index) => ( - - - - ))} - - + + {KPI_CHARTS.map(({ id, layers, title, toolTip }, index) => ( + + } + visualizationType="lnsMetric" + disableTriggers + hidePanelTitles + /> + + ))} + ); }); diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/tile.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/tile.tsx deleted file mode 100644 index 9907b81d64fc0..0000000000000 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/tile.tsx +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React, { useMemo } from 'react'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { i18n } from '@kbn/i18n'; -import { EuiIcon, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui'; -import styled from 'styled-components'; -import type { Action } from '@kbn/ui-actions-plugin/public'; -import { TimeRange } from '@kbn/es-query'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { LensWrapper, TooltipContent } from '../../../../lens'; -import type { KPIChartProps } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config'; -import { useLensAttributes } from '../../../../../hooks/use_lens_attributes'; -import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; - -const MIN_HEIGHT = 150; - -export interface TileProps { - timeRange: TimeRange; - dataView?: DataView; - nodeName: string; -} - -export const Tile = ({ - id, - layers, - title, - toolTip, - dataView, - nodeName, - timeRange, -}: KPIChartProps & TileProps) => { - const getSubtitle = () => - i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.metricTrend.subtitle.average', { - defaultMessage: 'Average', - }); - - const { formula, attributes, getExtraActions, error } = useLensAttributes({ - dataView, - title, - layers: { ...layers, options: { ...layers.options, subtitle: getSubtitle() } }, - visualizationType: 'lnsMetric', - }); - - const filters = useMemo(() => { - return [ - buildCombinedHostsFilter({ - field: 'host.name', - values: [nodeName], - dataView, - }), - ]; - }, [dataView, nodeName]); - - const extraActions: Action[] = useMemo( - () => - getExtraActions({ - timeRange, - filters, - }), - [filters, getExtraActions, timeRange] - ); - - const loading = !attributes; - - return ( - - {error ? ( - - - - - - - - - - - ) : ( - } - anchorClassName="eui-fullWidth" - > - - - )} - - ); -}; - -const EuiPanelStyled = styled(EuiPanel)` - min-height: ${MIN_HEIGHT}px; - .echMetric { - border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; - pointer-events: none; - } -`; diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_header.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_header.tsx index d3a0001f06db1..66f2c3585d62d 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_header.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_header.tsx @@ -55,7 +55,7 @@ export const MetadataHeader = ({ metadataValue }: MetadataSummaryProps) => { @@ -72,7 +72,7 @@ export const MetadataHeader = ({ metadataValue }: MetadataSummaryProps) => { values={{ documentation: ( diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx index 1aedec3f05037..ee7210dd2d8de 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx @@ -78,7 +78,7 @@ export const MetadataSummaryList = ({ metadata, metadataLoading }: MetadataSumma { - title: string; - layers: Array>; - dataView?: DataView; - timeRange: TimeRange; - nodeName: string; -} - -const MIN_HEIGHT = 250; - -export const MetricChart = ({ - id, - title, - layers, - nodeName, - timeRange, - dataView, - overrides, -}: MetricChartProps) => { - const { euiTheme } = useEuiTheme(); - - const { attributes, getExtraActions, error } = useLensAttributes({ - dataView, - layers, - title, - visualizationType: 'lnsXY', - }); - - const filters = useMemo(() => { - return [ - buildCombinedHostsFilter({ - field: 'host.name', - values: [nodeName], - dataView, - }), - ]; - }, [dataView, nodeName]); - - const extraActions: Action[] = useMemo( - () => - getExtraActions({ - timeRange, - filters, - }), - [timeRange, filters, getExtraActions] - ); - - const loading = !attributes; - - return ( - - {error ? ( - - - - - - - - - - - ) : ( - - )} - - ); -}; diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx index 9a62fe1d2fe72..5f0bce1b54856 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx @@ -4,18 +4,30 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { EuiFlexGrid, EuiFlexItem, EuiTitle, EuiSpacer, EuiFlexGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { TimeRange } from '@kbn/es-query'; +import type { TimeRange } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; -import { HostMetricsDocsLink } from '../../../../lens'; -import { MetricChart, type MetricChartProps } from './metric_chart'; -import { hostLensFormulas } from '../../../../../common/visualizations'; +import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; +import type { Layer } from '../../../../../hooks/use_lens_attributes'; +import { HostMetricsDocsLink, LensChart, type LensChartProps } from '../../../../lens'; +import { + type FormulaConfig, + hostLensFormulas, + type XYLayerOptions, +} from '../../../../../common/visualizations'; +import { METRIC_CHART_HEIGHT } from '../../../constants'; -const PERCENT_LEFT_AXIS: Pick['overrides'] = { +type DataViewOrigin = 'logs' | 'metrics'; +interface MetricChartConfig extends Pick { + layers: Array>; + toolTip: string; +} + +const PERCENT_LEFT_AXIS: Pick['overrides'] = { axisLeft: { domain: { min: 0, @@ -24,7 +36,7 @@ const PERCENT_LEFT_AXIS: Pick['overrides'] = { }, }; -const LEGEND_SETTINGS: Pick['overrides'] = { +const LEGEND_SETTINGS: Pick['overrides'] = { settings: { showLegend: true, legendPosition: 'bottom', @@ -33,8 +45,8 @@ const LEGEND_SETTINGS: Pick['overrides'] = { }; const CHARTS_IN_ORDER: Array< - Pick & { - dataViewType: 'logs' | 'metrics'; + Pick & { + dataViewOrigin: DataViewOrigin; } > = [ { @@ -49,7 +61,7 @@ const CHARTS_IN_ORDER: Array< layerType: 'data', }, ], - dataViewType: 'metrics', + dataViewOrigin: 'metrics', overrides: { axisLeft: PERCENT_LEFT_AXIS.axisLeft, }, @@ -65,7 +77,7 @@ const CHARTS_IN_ORDER: Array< layerType: 'data', }, ], - dataViewType: 'metrics', + dataViewOrigin: 'metrics', overrides: { axisLeft: PERCENT_LEFT_AXIS.axisLeft, }, @@ -96,7 +108,7 @@ const CHARTS_IN_ORDER: Array< layerType: 'referenceLine', }, ], - dataViewType: 'metrics', + dataViewOrigin: 'metrics', }, { id: 'logRate', @@ -109,7 +121,7 @@ const CHARTS_IN_ORDER: Array< layerType: 'data', }, ], - dataViewType: 'logs', + dataViewOrigin: 'logs', }, { id: 'diskSpaceUsageAvailable', @@ -126,7 +138,7 @@ const CHARTS_IN_ORDER: Array< }), }, { - ...hostLensFormulas.diskSpaceAvailable, + ...hostLensFormulas.diskSpaceAvailability, label: i18n.translate( 'xpack.infra.assetDetails.metricsCharts.diskSpace.label.available', { @@ -152,7 +164,7 @@ const CHARTS_IN_ORDER: Array< axisLeft: PERCENT_LEFT_AXIS.axisLeft, settings: LEGEND_SETTINGS.settings, }, - dataViewType: 'metrics', + dataViewOrigin: 'metrics', }, { id: 'diskThroughputReadWrite', @@ -163,13 +175,13 @@ const CHARTS_IN_ORDER: Array< { data: [ { - ...hostLensFormulas.diskReadThroughput, + ...hostLensFormulas.diskIORead, label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { defaultMessage: 'Read', }), }, { - ...hostLensFormulas.diskWriteThroughput, + ...hostLensFormulas.diskIOWrite, label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { defaultMessage: 'Write', }), @@ -184,7 +196,7 @@ const CHARTS_IN_ORDER: Array< overrides: { settings: LEGEND_SETTINGS.settings, }, - dataViewType: 'metrics', + dataViewOrigin: 'metrics', }, { id: 'diskIOReadWrite', @@ -195,13 +207,13 @@ const CHARTS_IN_ORDER: Array< { data: [ { - ...hostLensFormulas.diskIORead, + ...hostLensFormulas.diskReadThroughput, label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { defaultMessage: 'Read', }), }, { - ...hostLensFormulas.diskIOWrite, + ...hostLensFormulas.diskWriteThroughput, label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { defaultMessage: 'Write', }), @@ -216,7 +228,7 @@ const CHARTS_IN_ORDER: Array< overrides: { settings: LEGEND_SETTINGS.settings, }, - dataViewType: 'metrics', + dataViewOrigin: 'metrics', }, { id: 'rxTx', @@ -248,7 +260,7 @@ const CHARTS_IN_ORDER: Array< overrides: { settings: LEGEND_SETTINGS.settings, }, - dataViewType: 'metrics', + dataViewOrigin: 'metrics', }, ]; @@ -259,8 +271,35 @@ export interface MetricsGridProps { logsDataView?: DataView; } +export interface MetricsGridProps { + nodeName: string; + timeRange: TimeRange; + metricsDataView?: DataView; + logsDataView?: DataView; +} + export const MetricsGrid = React.memo( ({ nodeName, metricsDataView, logsDataView, timeRange }: MetricsGridProps) => { + const getDataView = useCallback( + (dataViewOrigin: DataViewOrigin) => { + return dataViewOrigin === 'metrics' ? metricsDataView : logsDataView; + }, + [logsDataView, metricsDataView] + ); + + const getFilters = useCallback( + (dataViewOrigin: DataViewOrigin) => { + return [ + buildCombinedHostsFilter({ + field: 'host.name', + values: [nodeName], + dataView: getDataView(dataViewOrigin), + }), + ]; + }, + [getDataView, nodeName] + ); + return ( @@ -276,14 +315,25 @@ export const MetricsGrid = React.memo( - - {CHARTS_IN_ORDER.map(({ dataViewType, ...chartProp }, index) => ( + + {CHARTS_IN_ORDER.map(({ dataViewOrigin, id, layers, title, overrides }, index) => ( - ))} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx index 3a9a642675993..5485a76a71dd3 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx @@ -21,18 +21,18 @@ import { toTimestampRange } from '../../utils'; import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state'; export const Overview = () => { - const { node, nodeType, overrides, dateRange } = useAssetDetailsStateContext(); + const { asset, assetType, overrides, dateRange } = useAssetDetailsStateContext(); const { logsDataView, metricsDataView } = overrides?.overview ?? {}; - const inventoryModel = findInventoryModel(nodeType); + const inventoryModel = findInventoryModel(assetType); const { sourceId } = useSourceContext(); const { loading: metadataLoading, error: fetchMetadataError, metadata, } = useMetadata( - node.name, - nodeType, + asset.name, + assetType, inventoryModel.requiredMetrics, sourceId, toTimestampRange(dateRange) @@ -41,7 +41,7 @@ export const Overview = () => { return ( - + {fetchMetadataError ? ( @@ -59,7 +59,7 @@ export const Overview = () => { values={{ reload: ( window.location.reload()} > {i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.errorAction', { @@ -76,7 +76,7 @@ export const Overview = () => { - + @@ -84,7 +84,7 @@ export const Overview = () => { timeRange={dateRange} logsDataView={logsDataView} metricsDataView={metricsDataView} - nodeName={node.name} + nodeName={asset.name} /> diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx index b91c96c3c948e..7bb8e96276fd8 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx @@ -35,7 +35,7 @@ const options = Object.entries(STATE_NAMES).map(([value, view]: [string, string] })); export const Processes = () => { - const { node, nodeType, overrides, dateRangeTs, onTabsStateChange } = + const { asset, assetType, overrides, dateRangeTs, onTabsStateChange } = useAssetDetailsStateContext(); const { query: overrideQuery } = overrides?.processes ?? {}; @@ -52,9 +52,9 @@ export const Processes = () => { }); const hostTerm = useMemo(() => { - const field = getFieldByType(nodeType) ?? nodeType; - return { [field]: node.name }; - }, [node.name, nodeType]); + const field = getFieldByType(assetType) ?? assetType; + return { [field]: asset.name }; + }, [asset.name, assetType]); const { loading, @@ -159,7 +159,7 @@ export const Processes = () => { } actions={ - + {columns.map((column) => ( diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/summary_table.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/summary_table.tsx index 57814aa7bacc2..928f48307eee7 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/summary_table.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/summary_table.tsx @@ -59,7 +59,10 @@ export const SummaryTable = ({ processSummary, isLoading }: Props) => { {Object.entries(processCount).map(([field, value]) => ( - + {columnTitles[field as keyof SummaryRecord]} {value === -1 ? : value} diff --git a/x-pack/plugins/infra/public/components/asset_details/types.ts b/x-pack/plugins/infra/public/components/asset_details/types.ts index ebd3c8823b8ca..cbf66d66f0a27 100644 --- a/x-pack/plugins/infra/public/components/asset_details/types.ts +++ b/x-pack/plugins/infra/public/components/asset_details/types.ts @@ -13,7 +13,7 @@ import type { InventoryItemType } from '../../../common/inventory_models/types'; interface Metadata { ip?: string | null; } -export type Node = Metadata & { +export type Asset = Metadata & { id: string; name: string; }; @@ -60,11 +60,11 @@ export interface TabState { export interface FlyoutProps { closeFlyout: () => void; - showInFlyout: true; + mode: 'flyout'; } export interface FullPageProps { - showInFlyout: false; + mode: 'page'; } export type RenderMode = FlyoutProps | FullPageProps; @@ -72,14 +72,13 @@ export type RenderMode = FlyoutProps | FullPageProps; export interface Tab { id: FlyoutTabIds; name: string; - 'data-test-subj': string; } export type LinkOptions = 'alertRule' | 'nodeDetails' | 'apmServices'; export interface AssetDetailsProps { - node: Node; - nodeType: InventoryItemType; + asset: Asset; + assetType: InventoryItemType; dateRange: TimeRange; tabs: Tab[]; activeTabId?: TabIds; diff --git a/x-pack/plugins/infra/public/components/lens/chart_load_error.tsx b/x-pack/plugins/infra/public/components/lens/chart_load_error.tsx new file mode 100644 index 0000000000000..5ffdc573a2cd7 --- /dev/null +++ b/x-pack/plugins/infra/public/components/lens/chart_load_error.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; + +export const ChartLoadError = () => { + return ( + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/components/lens/chart_placeholder.tsx b/x-pack/plugins/infra/public/components/lens/chart_placeholder.tsx index 7bf35f0e0392a..2dfcbfa21c814 100644 --- a/x-pack/plugins/infra/public/components/lens/chart_placeholder.tsx +++ b/x-pack/plugins/infra/public/components/lens/chart_placeholder.tsx @@ -6,8 +6,7 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiProgress, EuiFlexItem, EuiLoadingChart } from '@elastic/eui'; -import { useEuiTheme } from '@elastic/eui'; +import { EuiFlexGroup, EuiProgress, EuiFlexItem, EuiLoadingChart, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; export const ChartLoadingProgress = ({ hasTopMargin = false }: { hasTopMargin?: boolean }) => { diff --git a/x-pack/plugins/infra/public/components/lens/index.tsx b/x-pack/plugins/infra/public/components/lens/index.tsx index 17a2f5b480442..93d050209a219 100644 --- a/x-pack/plugins/infra/public/components/lens/index.tsx +++ b/x-pack/plugins/infra/public/components/lens/index.tsx @@ -5,8 +5,7 @@ * 2.0. */ +export { LensChart, type LensChartProps } from './lens_chart'; export { ChartPlaceholder } from './chart_placeholder'; -export { LensWrapper } from './lens_wrapper'; - export { TooltipContent } from './metric_explanation/tooltip_content'; export { HostMetricsDocsLink } from './metric_explanation/host_metrics_docs_link'; diff --git a/x-pack/plugins/infra/public/components/lens/lens_chart.tsx b/x-pack/plugins/infra/public/components/lens/lens_chart.tsx new file mode 100644 index 0000000000000..2d4b599d56de2 --- /dev/null +++ b/x-pack/plugins/infra/public/components/lens/lens_chart.tsx @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { CSSProperties, useMemo } from 'react'; +import { EuiPanel, EuiToolTip, type EuiPanelProps } from '@elastic/eui'; +import { Action } from '@kbn/ui-actions-plugin/public'; +import { css } from '@emotion/react'; +import { useLensAttributes, type UseLensAttributesParams } from '../../hooks/use_lens_attributes'; +import type { BaseChartProps } from './types'; +import type { TooltipContentProps } from './metric_explanation/tooltip_content'; +import { LensWrapper } from './lens_wrapper'; +import { ChartLoadError } from './chart_load_error'; + +const MIN_HEIGHT = 300; + +export type LensChartProps = UseLensAttributesParams & + BaseChartProps & + Pick & { + toolTip?: React.ReactElement; + }; + +export const LensChart = ({ + id, + borderRadius, + dateRange, + filters, + hidePanelTitles, + lastReloadRequestTime, + query, + onBrushEnd, + overrides, + toolTip, + disableTriggers = false, + height = MIN_HEIGHT, + loading = false, + ...lensAttributesParams +}: LensChartProps) => { + const { formula, attributes, getExtraActions, error } = useLensAttributes({ + ...lensAttributesParams, + }); + + const isLoading = loading || !attributes; + + const extraActions: Action[] = useMemo( + () => + getExtraActions({ + timeRange: dateRange, + query, + filters, + }), + [dateRange, filters, getExtraActions, query] + ); + + const sytle: CSSProperties = useMemo(() => ({ height }), [height]); + + const Lens = ( + + ); + + const getContent = () => { + if (!toolTip) { + return Lens; + } + + return ( + + {Lens} + + ); + }; + + return ( + + {error ? : getContent()} + + ); +}; diff --git a/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx b/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx index dc3c11dccacc0..f203c9c344797 100644 --- a/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx +++ b/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx @@ -9,7 +9,8 @@ import type { Action } from '@kbn/ui-actions-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import type { TimeRange } from '@kbn/es-query'; import { TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { css } from '@emotion/react'; +import { useEuiTheme } from '@elastic/eui'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { ChartLoadingProgress, ChartPlaceholder } from './chart_placeholder'; import { parseDateRange } from '../../utils/datemath'; @@ -30,10 +31,11 @@ export const LensWrapper = ({ dateRange, filters, lastReloadRequestTime, - loading, + loading = false, query, ...props }: LensWrapperProps) => { + const { euiTheme } = useEuiTheme(); const [intersectionObserverEntry, setIntersectionObserverEntry] = useState(); const [embeddableLoaded, setEmbeddableLoaded] = useState(false); @@ -96,11 +98,25 @@ export const LensWrapper = ({ return { from, to }; }, [state.dateRange]); - const isLoading = loading || !state.attributes; return ( - +
<> {isLoading && !embeddableLoaded ? ( @@ -120,7 +136,7 @@ export const LensWrapper = ({ )} - +
); }; @@ -142,13 +158,3 @@ const EmbeddableComponentMemo = React.memo( return ; } ); - -const Container = euiStyled.div` - position: relative; - border-radius: ${({ theme }) => theme.eui.euiSizeS}; - overflow: hidden; - height: 100%; - .echLegend .echLegendList { - display: flex; - } -`; diff --git a/x-pack/plugins/infra/public/components/lens/metric_explanation/tooltip_content.tsx b/x-pack/plugins/infra/public/components/lens/metric_explanation/tooltip_content.tsx index fd46700130ee4..52459e70e2b05 100644 --- a/x-pack/plugins/infra/public/components/lens/metric_explanation/tooltip_content.tsx +++ b/x-pack/plugins/infra/public/components/lens/metric_explanation/tooltip_content.tsx @@ -11,14 +11,14 @@ import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import { HOST_METRICS_DOC_HREF } from '../../../common/visualizations/constants'; -interface Props extends Pick, 'style'> { +export interface TooltipContentProps extends Pick, 'style'> { description: string; formula?: string; showDocumentationLink?: boolean; } export const TooltipContent = React.memo( - ({ description, formula, showDocumentationLink = false, style }: Props) => { + ({ description, formula, showDocumentationLink = false, style }: TooltipContentProps) => { const onClick = (e: React.MouseEvent) => { e.stopPropagation(); }; diff --git a/x-pack/plugins/infra/public/components/lens/types.ts b/x-pack/plugins/infra/public/components/lens/types.ts new file mode 100644 index 0000000000000..8a4791de8cdf5 --- /dev/null +++ b/x-pack/plugins/infra/public/components/lens/types.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DataView } from '@kbn/data-views-plugin/public'; +import type { LensWrapperProps } from './lens_wrapper'; + +export type BaseChartProps = Pick< + LensWrapperProps, + | 'id' + | 'dateRange' + | 'disableTriggers' + | 'filters' + | 'hidePanelTitles' + | 'lastReloadRequestTime' + | 'loading' + | 'overrides' + | 'onBrushEnd' + | 'query' + | 'title' +> & { + dataView?: DataView; + height?: number; +}; diff --git a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts index 721bf5669d203..6f3ae0cf37aff 100644 --- a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts +++ b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts @@ -68,7 +68,9 @@ interface UseLensAttributesMetricChartParams visualizationType: 'lnsMetric'; } -type UseLensAttributesParams = UseLensAttributesXYChartParams | UseLensAttributesMetricChartParams; +export type UseLensAttributesParams = + | UseLensAttributesXYChartParams + | UseLensAttributesMetricChartParams; export const useLensAttributes = ({ dataView, diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx index 97be988142aa0..e78aae020b46b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx @@ -6,8 +6,8 @@ */ import React, { useEffect, useRef, CSSProperties } from 'react'; import { Chart, Metric, type MetricWNumber, type MetricWTrend } from '@elastic/charts'; -import { EuiPanel, EuiToolTip } from '@elastic/eui'; -import styled from 'styled-components'; +import { EuiPanel, EuiToolTip, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; import { ChartPlaceholder } from '../../../../../components/lens'; export interface Props extends Pick { @@ -16,11 +16,11 @@ export interface Props extends Pick { + const euiTheme = useEuiTheme(); const loadedOnce = useRef(false); useEffect(() => { @@ -42,7 +42,7 @@ export const MetricChartWrapper = React.memo( }; return ( - + {loading && !loadedOnce.current ? ( ) : ( @@ -52,19 +52,20 @@ export const MetricChartWrapper = React.memo( content={toolTip} anchorClassName="eui-fullWidth" > - + - + )} ); } ); - -const KPIChartStyled = styled(Chart)` - .echMetric { - border-radius: ${(p) => p.theme.eui.euiBorderRadius}; - pointer-events: none; - } -`; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx index a1d8542130c14..884e0dd389cd3 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx @@ -7,7 +7,6 @@ import React from 'react'; import useAsync from 'react-use/lib/useAsync'; -import type { InventoryItemType } from '../../../../../../common/inventory_models/types'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import type { HostNodeRow } from '../../hooks/use_hosts_table'; import { HostFlyout, useHostFlyoutUrlState } from '../../hooks/use_host_flyout_url_state'; @@ -21,8 +20,6 @@ export interface Props { closeFlyout: () => void; } -const NODE_TYPE = 'host' as InventoryItemType; - export const FlyoutWrapper = ({ node, closeFlyout }: Props) => { const { searchCriteria } = useUnifiedSearchContext(); const { dataView } = useMetricsDataViewContext(); @@ -39,8 +36,8 @@ export const FlyoutWrapper = ({ node, closeFlyout }: Props) => { return ( { tabs={orderedFlyoutTabs} links={['apmServices', 'nodeDetails']} renderMode={{ - showInFlyout: true, + mode: 'flyout', closeFlyout, }} /> diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts index 4445e5fba924a..7d354d19bed12 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts @@ -14,41 +14,35 @@ export const orderedFlyoutTabs: Tab[] = [ name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', { defaultMessage: 'Overview', }), - 'data-test-subj': 'hostsView-flyout-tabs-overview', }, { id: FlyoutTabIds.METADATA, name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', { defaultMessage: 'Metadata', }), - 'data-test-subj': 'hostsView-flyout-tabs-metadata', }, { id: FlyoutTabIds.PROCESSES, name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', { defaultMessage: 'Processes', }), - 'data-test-subj': 'hostsView-flyout-tabs-processes', }, { id: FlyoutTabIds.LOGS, name: i18n.translate('xpack.infra.nodeDetails.tabs.logs.title', { defaultMessage: 'Logs', }), - 'data-test-subj': 'hostsView-flyout-tabs-logs', }, { id: FlyoutTabIds.ANOMALIES, name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', { defaultMessage: 'Anomalies', }), - 'data-test-subj': 'hostsView-flyout-tabs-anomalies', }, { id: FlyoutTabIds.OSQUERY, name: i18n.translate('xpack.infra.nodeDetails.tabs.osquery', { defaultMessage: 'Osquery', }), - 'data-test-subj': 'hostsView-flyout-tabs-Osquery', }, ]; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx index 0571733b80034..f38e6772a3c84 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx @@ -6,7 +6,6 @@ */ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { KPIChartProps } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config'; import { hostLensFormulas } from '../../../../../common/visualizations'; import { useHostCountContext } from '../../hooks/use_host_count'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; @@ -16,21 +15,20 @@ import { type Props, MetricChartWrapper } from '../chart/metric_chart_wrapper'; import { TooltipContent } from '../../../../../components/lens'; const HOSTS_CHART: Omit = { - id: `metric-hostCount`, + id: 'hostsViewKPI-hostsCount', color: '#6DCCB1', - title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.title', { + title: i18n.translate('xpack.infra.hostsViewPage.kpi.hostCount.title', { defaultMessage: 'Hosts', }), - ['data-test-subj']: 'hostsViewKPI-hostsCount', }; -export const HostsTile = ({ style }: Pick) => { +export const HostsTile = ({ height }: { height: number }) => { const { data: hostCountData, isRequestRunning: hostCountLoading } = useHostCountContext(); const { searchCriteria } = useUnifiedSearchContext(); const getSubtitle = () => { return searchCriteria.limit < (hostCountData?.count.value ?? 0) - ? i18n.translate('xpack.infra.hostsViewPage.metricTrend.subtitle.hostCount.limit', { + ? i18n.translate('xpack.infra.hostsViewPage.kpi.subtitle.hostCount.limit', { defaultMessage: 'Limited to {limit}', values: { limit: searchCriteria.limit, @@ -42,7 +40,7 @@ export const HostsTile = ({ style }: Pick) => { return ( { return ( @@ -26,11 +24,11 @@ export const KPIGrid = () => { - + {KPI_CHARTS.map((chartProp, index) => ( - + ))} diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx index 950e74478dd4b..7211c89ce9071 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx @@ -4,53 +4,47 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useMemo, useCallback } from 'react'; - +import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { BrushTriggerEvent } from '@kbn/charts-plugin/public'; -import { EuiIcon, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui'; -import styled from 'styled-components'; -import { Action } from '@kbn/ui-actions-plugin/public'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { LensWrapper, TooltipContent } from '../../../../../components/lens'; -import { KPIChartProps } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config'; +import { LensChart, TooltipContent } from '../../../../../components/lens'; +import { + type KPIChartProps, + AVERAGE_SUBTITLE, +} from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config'; import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; -import { useLensAttributes } from '../../../../../hooks/use_lens_attributes'; import { useMetricsDataViewContext } from '../../hooks/use_data_view'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import { useHostsViewContext } from '../../hooks/use_hosts_view'; import { useHostCountContext } from '../../hooks/use_host_count'; import { useAfterLoadedState } from '../../hooks/use_after_loaded_state'; -import { KPI_CHART_MIN_HEIGHT } from '../../constants'; -export const Tile = ({ id, title, layers, style, toolTip }: KPIChartProps) => { - const { searchCriteria, onSubmit } = useUnifiedSearchContext(); +export const Tile = ({ + id, + title, + layers, + toolTip, + height, +}: KPIChartProps & { height: number }) => { + const { searchCriteria } = useUnifiedSearchContext(); const { dataView } = useMetricsDataViewContext(); const { requestTs, hostNodes, loading: hostsLoading } = useHostsViewContext(); const { data: hostCountData, isRequestRunning: hostCountLoading } = useHostCountContext(); const shouldUseSearchCriteria = hostNodes.length === 0; + const loading = hostsLoading || hostCountLoading; + const getSubtitle = () => { return searchCriteria.limit < (hostCountData?.count.value ?? 0) - ? i18n.translate('xpack.infra.hostsViewPage.metricTrend.subtitle.average.limit', { + ? i18n.translate('xpack.infra.hostsViewPage.kpi.subtitle.average.limit', { defaultMessage: 'Average (of {limit} hosts)', values: { limit: searchCriteria.limit, }, }) - : i18n.translate('xpack.infra.hostsViewPage.metricTrend.subtitle.average', { - defaultMessage: 'Average', - }); + : AVERAGE_SUBTITLE; }; - const { formula, attributes, getExtraActions, error } = useLensAttributes({ - dataView, - title, - layers: { ...layers, options: { ...layers.options, subtitle: getSubtitle() } }, - visualizationType: 'lnsMetric', - }); - const filters = useMemo(() => { return shouldUseSearchCriteria ? searchCriteria.filters @@ -61,106 +55,33 @@ export const Tile = ({ id, title, layers, style, toolTip }: KPIChartProps) => { dataView, }), ]; - }, [shouldUseSearchCriteria, searchCriteria.filters, hostNodes, dataView]); - - const loading = hostsLoading || !attributes || hostCountLoading; + }, [dataView, hostNodes, searchCriteria.filters, shouldUseSearchCriteria]); - // prevents requestTs and serchCriteria states from reloading the chart - // we want it to reload only once the host count and table have finished loading + // prevents requestTs and searchCriteria state from reloading the chart + // we want it to reload only once the table has finished loading const { afterLoadedState } = useAfterLoadedState(loading, { - attributes, lastReloadRequestTime: requestTs, - ...searchCriteria, + dateRange: searchCriteria.dateRange, + query: shouldUseSearchCriteria ? searchCriteria.query : undefined, filters, }); - const extraActions: Action[] = useMemo( - () => - getExtraActions({ - timeRange: afterLoadedState.dateRange, - query: shouldUseSearchCriteria ? afterLoadedState.query : undefined, - filters, - }), - [ - afterLoadedState.dateRange, - afterLoadedState.query, - filters, - getExtraActions, - shouldUseSearchCriteria, - ] - ); - - const handleBrushEnd = useCallback( - ({ range }: BrushTriggerEvent['data']) => { - const [min, max] = range; - onSubmit({ - dateRange: { - from: new Date(min).toISOString(), - to: new Date(max).toISOString(), - mode: 'absolute', - }, - }); - }, - [onSubmit] - ); - return ( - - {error ? ( - - - - - - - - - - - ) : ( - } - anchorClassName="eui-fullWidth" - > -
- -
-
- )} -
+ } + visualizationType="lnsMetric" + disableTriggers + hidePanelTitles + /> ); }; - -const EuiPanelStyled = styled(EuiPanel)` - min-height: ${KPI_CHART_MIN_HEIGHT}px; - .echMetric { - border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; - pointer-events: none; - } -`; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx index b31e5c1cb08d2..c0f05c55d1e9e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx @@ -4,59 +4,35 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { CSSProperties, useCallback, useMemo } from 'react'; -import { Action } from '@kbn/ui-actions-plugin/public'; -import { BrushTriggerEvent } from '@kbn/charts-plugin/public'; -import { EuiIcon, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { LensWrapper } from '../../../../../../components/lens'; -import { useLensAttributes, Layer } from '../../../../../../hooks/use_lens_attributes'; +import React, { useMemo } from 'react'; +import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import { LensChart } from '../../../../../../components/lens'; +import type { Layer } from '../../../../../../hooks/use_lens_attributes'; import { useMetricsDataViewContext } from '../../../hooks/use_data_view'; import { useUnifiedSearchContext } from '../../../hooks/use_unified_search'; -import { FormulaConfig, XYLayerOptions } from '../../../../../../common/visualizations'; +import type { FormulaConfig, XYLayerOptions } from '../../../../../../common/visualizations'; import { useHostsViewContext } from '../../../hooks/use_hosts_view'; import { buildCombinedHostsFilter } from '../../../../../../utils/filters/build'; import { useHostsTableContext } from '../../../hooks/use_hosts_table'; import { useAfterLoadedState } from '../../../hooks/use_after_loaded_state'; -import { METRIC_CHART_MIN_HEIGHT } from '../../../constants'; +import { METRIC_CHART_HEIGHT } from '../../../constants'; export interface MetricChartProps extends Pick { title: string; layers: Array>; } -const lensStyle: CSSProperties = { - height: METRIC_CHART_MIN_HEIGHT, -}; - export const MetricChart = ({ id, title, layers, overrides }: MetricChartProps) => { - const { euiTheme } = useEuiTheme(); - const { searchCriteria, onSubmit } = useUnifiedSearchContext(); + const { searchCriteria } = useUnifiedSearchContext(); const { dataView } = useMetricsDataViewContext(); const { requestTs, loading } = useHostsViewContext(); const { currentPage } = useHostsTableContext(); const shouldUseSearchCriteria = currentPage.length === 0; - // prevents requestTs and serchCriteria states from reloading the chart - // we want it to reload only once the table has finished loading - const { afterLoadedState } = useAfterLoadedState(loading, { - lastReloadRequestTime: requestTs, - ...searchCriteria, - }); - - const { attributes, getExtraActions, error } = useLensAttributes({ - dataView, - layers, - title, - visualizationType: 'lnsXY', - }); - const filters = useMemo(() => { return shouldUseSearchCriteria - ? afterLoadedState.filters + ? searchCriteria.filters : [ buildCombinedHostsFilter({ field: 'host.name', @@ -64,85 +40,31 @@ export const MetricChart = ({ id, title, layers, overrides }: MetricChartProps) dataView, }), ]; - }, [afterLoadedState.filters, currentPage, dataView, shouldUseSearchCriteria]); - - const extraActions: Action[] = useMemo( - () => - getExtraActions({ - timeRange: afterLoadedState.dateRange, - query: shouldUseSearchCriteria ? afterLoadedState.query : undefined, - filters, - }), - [ - afterLoadedState.dateRange, - afterLoadedState.query, - filters, - getExtraActions, - shouldUseSearchCriteria, - ] - ); + }, [searchCriteria.filters, currentPage, dataView, shouldUseSearchCriteria]); - const handleBrushEnd = useCallback( - ({ range }: BrushTriggerEvent['data']) => { - const [min, max] = range; - onSubmit({ - dateRange: { - from: new Date(min).toISOString(), - to: new Date(max).toISOString(), - mode: 'absolute', - }, - }); - }, - [onSubmit] - ); + // prevents requestTs and searchCriteria state from reloading the chart + // we want it to reload only once the table has finished loading + const { afterLoadedState } = useAfterLoadedState(loading, { + lastReloadRequestTime: requestTs, + dateRange: searchCriteria.dateRange, + query: shouldUseSearchCriteria ? searchCriteria.query : undefined, + }); return ( - - {error ? ( - - - - - - - - - - - ) : ( - - )} - + dataView={dataView} + dateRange={afterLoadedState.dateRange} + height={METRIC_CHART_HEIGHT} + layers={layers} + lastReloadRequestTime={afterLoadedState.lastReloadRequestTime} + loading={loading} + filters={filters} + query={afterLoadedState.query} + title={title} + overrides={overrides} + visualizationType="lnsXY" + /> ); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts index 296d779e8a0fd..aace07448692e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts @@ -15,8 +15,7 @@ export const DEFAULT_PAGE_SIZE = 10; export const LOCAL_STORAGE_HOST_LIMIT_KEY = 'hostsView:hostLimitSelection'; export const LOCAL_STORAGE_PAGE_SIZE_KEY = 'hostsView:pageSizeSelection'; -export const KPI_CHART_MIN_HEIGHT = 150; -export const METRIC_CHART_MIN_HEIGHT = 300; +export const METRIC_CHART_HEIGHT = 300; export const HOST_LIMIT_OPTIONS = [50, 100, 500] as const; export const HOST_METRICS_DOC_HREF = 'https://ela.st/docs-infra-host-metrics'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx index e3ca9b770e2ef..6806fbd31d79a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx @@ -86,6 +86,7 @@ export const WaffleInventorySwitcher: React.FC = () => { { name: 'AWS', panel: 'awsPanel', + 'data-test-subj': 'goToAWS-open', }, ], }, @@ -96,18 +97,22 @@ export const WaffleInventorySwitcher: React.FC = () => { { name: getDisplayNameForType('awsEC2'), onClick: goToAwsEC2, + 'data-test-subj': 'goToAWS-EC2', }, { name: getDisplayNameForType('awsS3'), onClick: goToAwsS3, + 'data-test-subj': 'goToAWS-S3', }, { name: getDisplayNameForType('awsRDS'), onClick: goToAwsRDS, + 'data-test-subj': 'goToAWS-RDS', }, { name: getDisplayNameForType('awsSQS'), onClick: goToAwsSQS, + 'data-test-subj': 'goToAWS-SQS', }, ], }, diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx index c8faac5327de9..4e1328fc3ae7e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx @@ -9,6 +9,7 @@ import { EuiErrorBoundary } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; +import { FeatureFeedbackButton } from '../../../components/feature_feedback_button'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useMetricsExplorerViews } from '../../../hooks/use_metrics_explorer_views'; import { MetricsSourceConfigurationProperties } from '../../../../common/metrics_sources'; @@ -27,6 +28,8 @@ interface MetricsExplorerPageProps { derivedIndexPattern: DerivedIndexPattern; } +const METRICS_EXPLORER_FEEDBACK_URL = 'https://ela.st/survey-infra-metricsexplorer?usp=pp_url'; + export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExplorerPageProps) => { const [enabled, setEnabled] = useState(false); const { @@ -91,7 +94,13 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl hasData={metricIndicesExist} pageHeader={{ pageTitle: metricsExplorerTitle, - rightSideItems: [], + rightSideItems: [ + , + , + ], }} > => ({ reportHostFlyoutFilterRemoved: jest.fn(), reportHostFlyoutFilterAdded: jest.fn(), reportHostsViewTotalHostCountRetrieved: jest.fn(), + reportAssetDetailsFlyoutViewed: jest.fn(), }); diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts index 56eb8d1af2c77..9107157c9835d 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts @@ -7,6 +7,7 @@ import { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import { + AssetDetailsFlyoutViewedParams, HostEntryClickedParams, HostFlyoutFilterActionParams, HostsViewQueryHostsCountRetrievedParams, @@ -60,4 +61,8 @@ export class TelemetryClient implements ITelemetryClient { params ); } + + public reportAssetDetailsFlyoutViewed = (params: AssetDetailsFlyoutViewedParams) => { + this.analytics.reportEvent(InfraTelemetryEventTypes.ASSET_DETAILS_FLYOUT_VIEWED, params); + }; } diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts index 4d55baf61674b..56cce313ec219 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts @@ -112,7 +112,35 @@ const hostViewTotalHostCountRetrieved: InfraTelemetryEvent = { }, }; +const assetDetailsFlyoutViewed: InfraTelemetryEvent = { + eventType: InfraTelemetryEventTypes.ASSET_DETAILS_FLYOUT_VIEWED, + schema: { + componentName: { + type: 'keyword', + _meta: { + description: 'Hostname for the clicked host.', + optional: false, + }, + }, + assetType: { + type: 'keyword', + _meta: { + description: 'Cloud provider for the clicked host.', + optional: false, + }, + }, + tabId: { + type: 'keyword', + _meta: { + description: 'Cloud provider for the clicked host.', + optional: true, + }, + }, + }, +}; + export const infraTelemetryEvents = [ + assetDetailsFlyoutViewed, hostsViewQuerySubmittedEvent, hostsEntryClickedEvent, hostFlyoutRemoveFilter, diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts index 6e2030a3fdaf3..b3c4b02468ca6 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts @@ -183,4 +183,28 @@ describe('TelemetryService', () => { ); }); }); + + describe('#reportAssetDetailsFlyoutViewed', () => { + it('should report asset details viewed with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + + telemetry.reportAssetDetailsFlyoutViewed({ + componentName: 'infraAssetDetailsFlyout', + assetType: 'host', + tabId: 'overview', + }); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + InfraTelemetryEventTypes.ASSET_DETAILS_FLYOUT_VIEWED, + { + componentName: 'infraAssetDetailsFlyout', + assetType: 'host', + tabId: 'overview', + } + ); + }); + }); }); diff --git a/x-pack/plugins/infra/public/services/telemetry/types.ts b/x-pack/plugins/infra/public/services/telemetry/types.ts index 0ccd6c0633b4a..2ecf8115eaa58 100644 --- a/x-pack/plugins/infra/public/services/telemetry/types.ts +++ b/x-pack/plugins/infra/public/services/telemetry/types.ts @@ -18,6 +18,7 @@ export enum InfraTelemetryEventTypes { HOST_FLYOUT_FILTER_REMOVED = 'Host Flyout Filter Removed', HOST_FLYOUT_FILTER_ADDED = 'Host Flyout Filter Added', HOST_VIEW_TOTAL_HOST_COUNT_RETRIEVED = 'Host View Total Host Count Retrieved', + ASSET_DETAILS_FLYOUT_VIEWED = 'Asset Details Flyout Viewed', } export interface HostsViewQuerySubmittedParams { @@ -41,11 +42,18 @@ export interface HostsViewQueryHostsCountRetrievedParams { total: number; } +export interface AssetDetailsFlyoutViewedParams { + assetType: string; + componentName: string; + tabId?: string; +} + export type InfraTelemetryEventParams = | HostsViewQuerySubmittedParams | HostEntryClickedParams | HostFlyoutFilterActionParams - | HostsViewQueryHostsCountRetrievedParams; + | HostsViewQueryHostsCountRetrievedParams + | AssetDetailsFlyoutViewedParams; export interface ITelemetryClient { reportHostEntryClicked(params: HostEntryClickedParams): void; @@ -53,6 +61,7 @@ export interface ITelemetryClient { reportHostFlyoutFilterAdded(params: HostFlyoutFilterActionParams): void; reportHostsViewTotalHostCountRetrieved(params: HostsViewQueryHostsCountRetrievedParams): void; reportHostsViewQuerySubmitted(params: HostsViewQuerySubmittedParams): void; + reportAssetDetailsFlyoutViewed(params: AssetDetailsFlyoutViewedParams): void; } export type InfraTelemetryEvent = @@ -75,4 +84,8 @@ export type InfraTelemetryEvent = | { eventType: InfraTelemetryEventTypes.HOST_VIEW_TOTAL_HOST_COUNT_RETRIEVED; schema: RootSchema; + } + | { + eventType: InfraTelemetryEventTypes.ASSET_DETAILS_FLYOUT_VIEWED; + schema: RootSchema; }; diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index 7978b6f3b0901..c0cc1cbf1b3c9 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -68,6 +68,7 @@ "@kbn/core-http-server", "@kbn/logs-shared-plugin", "@kbn/licensing-plugin", + "@kbn/aiops-utils", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/ingest_pipelines/public/application/app.tsx b/x-pack/plugins/ingest_pipelines/public/application/app.tsx index 0cb3c0a98ec8e..6b47ed277673e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/app.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/app.tsx @@ -6,7 +6,7 @@ */ import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiPageContent_Deprecated as EuiPageContent, EuiEmptyPrompt } from '@elastic/eui'; +import { EuiPageTemplate } from '@elastic/eui'; import React, { FunctionComponent } from 'react'; import { Router, Routes, Route } from '@kbn/shared-ux-router'; @@ -67,45 +67,41 @@ export const App: FunctionComponent = () => { {({ isLoading, hasPrivileges, privilegesMissing }) => { if (isLoading) { return ( - - - - - + + + ); } if (!hasPrivileges) { return ( - - - -
- } - body={ -

- + + + } + body={ +

+ -

- } - /> - + values={{ + missingPrivileges: privilegesMissing.cluster!.join(', '), + privilegesCount: privilegesMissing.cluster!.length, + }} + /> +

+ } + /> ); } diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx index e8a10a8b518c7..a5b9b1bb3bd97 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx @@ -8,7 +8,6 @@ import React, { FunctionComponent, useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { EuiPageContent_Deprecated as EuiPageContent } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { SectionLoading, useKibana, attemptToURIDecode } from '../../../shared_imports'; @@ -49,14 +48,12 @@ export const PipelinesClone: FunctionComponent> if (isLoading && isInitialRequest) { return ( - - - - - + + + ); } else { // We still show the create form even if we were not able to load the diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx index 1cead79ddb96d..1bab1918c0e9c 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx @@ -10,12 +10,11 @@ import { RouteComponentProps } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiPageHeader, - EuiEmptyPrompt, - EuiPageContent_Deprecated as EuiPageContent, EuiSpacer, EuiButton, EuiButtonEmpty, EuiCallOut, + EuiPageTemplate, } from '@elastic/eui'; import { Pipeline } from '../../../../common/types'; @@ -95,47 +94,39 @@ export const PipelinesEdit: React.FunctionComponent - - - - + + + ); } if (error) { return ( - - - - - } - body={

{error.message}

} - actions={ - - - - } - /> -
+ iconType="warning" + title={ +

+ +

+ } + body={

{error.message}

} + actions={ + + + + } + /> ); } diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx index 035c33df69169..abece34aa1574 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx @@ -8,14 +8,7 @@ import React, { FunctionComponent, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiEmptyPrompt, - EuiLink, - EuiPageContent_Deprecated as EuiPageContent, - EuiButton, - EuiPopover, - EuiContextMenu, -} from '@elastic/eui'; +import { EuiLink, EuiButton, EuiPopover, EuiContextMenu, EuiPageTemplate } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; import { ScopedHistory } from '@kbn/core/public'; import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public'; @@ -48,67 +41,62 @@ export const EmptyList: FunctionComponent = () => { ]; return ( - - - {i18n.translate('xpack.ingestPipelines.list.table.emptyPromptTitle', { - defaultMessage: 'Start by creating a pipeline', + + {i18n.translate('xpack.ingestPipelines.list.table.emptyPromptTitle', { + defaultMessage: 'Start by creating a pipeline', + })} + + } + body={ +

+ {' '} + + {i18n.translate('xpack.ingestPipelines.list.table.emptyPromptDocumentionLink', { + defaultMessage: 'Learn more', })} - - } - body={ -

- {' '} - - {i18n.translate('xpack.ingestPipelines.list.table.emptyPromptDocumentionLink', { - defaultMessage: 'Learn more', + +

+ } + actions={ + setShowPopover(false)} + button={ + setShowPopover((previousBool) => !previousBool)} + > + {i18n.translate('xpack.ingestPipelines.list.table.emptyCreatePipelineDropdownLabel', { + defaultMessage: 'Create pipeline', })} -
-

- } - actions={ - setShowPopover(false)} - button={ - setShowPopover((previousBool) => !previousBool)} - > - {i18n.translate( - 'xpack.ingestPipelines.list.table.emptyCreatePipelineDropdownLabel', - { - defaultMessage: 'Create pipeline', - } - )} - - } - panelPaddingSize="none" - repositionOnScroll - > - - - } - /> - + + } + panelPaddingSize="none" + repositionOnScroll + > + + + } + /> ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx index 998a7d7622711..00df4599c4702 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx @@ -11,14 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { Location } from 'history'; import { parse } from 'query-string'; -import { - EuiPageHeader, - EuiButtonEmpty, - EuiPageContent_Deprecated as EuiPageContent, - EuiEmptyPrompt, - EuiButton, - EuiSpacer, -} from '@elastic/eui'; +import { EuiPageHeader, EuiButtonEmpty, EuiButton, EuiSpacer, EuiPageTemplate } from '@elastic/eui'; import { Pipeline } from '../../../../common/types'; import { useKibana, SectionLoading } from '../../../shared_imports'; @@ -81,41 +74,38 @@ export const PipelinesList: React.FunctionComponent = ({ if (error) { return ( - - - - - } - body={

{error.message}

} - actions={ - - - - } - /> -
+ + + + } + body={

{error.message}

} + actions={ + + + + } + /> ); } if (isLoading) { return ( - - - - - + + + ); } diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index beff6ee8525d0..174c2307424a3 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -188,7 +188,7 @@ export function App({ ) { return actions.confirm( i18n.translate('xpack.lens.app.unsavedWorkMessage', { - defaultMessage: 'Leave Lens with unsaved work?', + defaultMessage: 'Leave with unsaved changes?', }), i18n.translate('xpack.lens.app.unsavedWorkTitle', { defaultMessage: 'Unsaved changes', @@ -676,16 +676,16 @@ export function App({ {isGoBackToVizEditorModalVisible && ( setIsGoBackToVizEditorModalVisible(false)} onConfirm={navigateToVizEditor} cancelButtonText={i18n.translate('xpack.lens.app.goBackModalCancelBtn', { defaultMessage: 'Cancel', })} - confirmButtonText={i18n.translate('xpack.lens.app.goBackModalTitle', { - defaultMessage: 'Discard changes?', + confirmButtonText={i18n.translate('xpack.lens.app.unsavedWorkConfirmBtn', { + defaultMessage: 'Discard changes', })} buttonColor="danger" defaultFocusedButton="confirm" @@ -693,7 +693,7 @@ export function App({ > {i18n.translate('xpack.lens.app.goBackModalMessage', { defaultMessage: - 'The changes you have made here are not backwards compatible with your original {contextOriginatingApp} visualization. Are you sure you want to discard these unsaved changes and return to {contextOriginatingApp}?', + 'Your changes here won’t work with your original {contextOriginatingApp} visualization. Leave with unsaved changes and return to {contextOriginatingApp}?', values: { contextOriginatingApp }, })} diff --git a/x-pack/plugins/lens/public/data_views_service/loader.test.ts b/x-pack/plugins/lens/public/data_views_service/loader.test.ts index e7e2bab166a70..f91d236986b11 100644 --- a/x-pack/plugins/lens/public/data_views_service/loader.test.ts +++ b/x-pack/plugins/lens/public/data_views_service/loader.test.ts @@ -159,6 +159,65 @@ describe('loader', () => { expect(cache.foo.getFieldByName('timestamp')!.meta).toEqual(true); }); + it('should move over any time series meta data', async () => { + const cache = await loadIndexPatterns({ + cache: {}, + patterns: ['foo'], + dataViews: { + get: jest.fn(async () => ({ + id: 'foo', + title: 'Foo index', + metaFields: ['timestamp'], + isPersisted: () => true, + toSpec: () => ({}), + typeMeta: {}, + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes_counter', + displayName: 'bytes_counter', + type: 'number', + aggregatable: true, + searchable: true, + timeSeriesMetric: 'counter', + }, + { + name: 'bytes_gauge', + displayName: 'bytes_gauge', + type: 'number', + aggregatable: true, + searchable: true, + timeSeriesMetric: 'gauge', + }, + { + name: 'dimension', + displayName: 'dimension', + type: 'string', + aggregatable: true, + searchable: true, + timeSeriesDimension: true, + }, + ], + })), + getIdsWithTitle: jest.fn(async () => ({ + id: 'foo', + title: 'Foo index', + })), + create: jest.fn(), + } as unknown as Pick, + }); + + expect(cache.foo.getFieldByName('bytes_counter')!.timeSeriesMetric).toEqual('counter'); + expect(cache.foo.getFieldByName('bytes_gauge')!.timeSeriesMetric).toEqual('gauge'); + expect(cache.foo.getFieldByName('dimension')!.timeSeriesDimension).toEqual(true); + }); + it('should call the refresh callback when loading new indexpatterns', async () => { const onIndexPatternRefresh = jest.fn(); await loadIndexPatterns({ diff --git a/x-pack/plugins/lens/public/data_views_service/loader.ts b/x-pack/plugins/lens/public/data_views_service/loader.ts index f38a84cc60b48..784c97d832e34 100644 --- a/x-pack/plugins/lens/public/data_views_service/loader.ts +++ b/x-pack/plugins/lens/public/data_views_service/loader.ts @@ -29,6 +29,7 @@ export function convertDataViewIntoLensIndexPattern( dataView: DataView, restrictionRemapper: (name: string) => string = onRestrictionMapping ): IndexPattern { + const metaKeys = new Set(dataView.metaFields); const newFields = dataView.fields .filter(isFieldLensCompatible) .map((field): IndexPatternField => { @@ -40,13 +41,14 @@ export function convertDataViewIntoLensIndexPattern( aggregatable: field.aggregatable, filterable: field.filterable, searchable: field.searchable, - meta: dataView.metaFields.includes(field.name), + meta: metaKeys.has(field.name), esTypes: field.esTypes, scripted: field.scripted, isMapped: field.isMapped, customLabel: field.customLabel, runtimeField: field.runtimeField, runtime: Boolean(field.runtimeField), + timeSeriesDimension: field.timeSeriesDimension, timeSeriesMetric: field.timeSeriesMetric, timeSeriesRollup: field.isRolledUpField, partiallyApplicableFunctions: field.isRolledUpField diff --git a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.test.tsx b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.test.tsx index 1a22e34e0a3ec..d307e0eb094a2 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.test.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.test.tsx @@ -17,12 +17,15 @@ import { FieldBasedIndexPatternColumn, termsOperation, staticValueOperation, + minOperation, } from '../operations/definitions'; import { FieldInput, getErrorMessage } from './field_input'; -import { createMockedIndexPattern } from '../mocks'; +import { createMockedIndexPattern, createMockedIndexPatternWithAdditionalFields } from '../mocks'; import { getOperationSupportMatrix } from '.'; import { GenericIndexPatternColumn, FormBasedLayer, FormBasedPrivateState } from '../types'; import { ReferenceBasedIndexPatternColumn } from '../operations/definitions/column_types'; +import { FieldSelect } from './field_select'; +import { IndexPattern, VisualizationDimensionGroupConfig } from '../../../types'; jest.mock('../operations/layer_helpers', () => { const original = jest.requireActual('../operations/layer_helpers'); @@ -41,7 +44,7 @@ const defaultProps = { incompleteField: null, incompleteOperation: undefined, incompleteParams: {}, - dimensionGroups: [], + dimensionGroups: [] as VisualizationDimensionGroupConfig[], groupId: 'any', operationDefinitionMap: { terms: termsOperation, @@ -49,6 +52,7 @@ const defaultProps = { count: countOperation, differences: derivativeOperation, staticValue: staticValueOperation, + min: minOperation, } as unknown as Record, }; @@ -102,9 +106,12 @@ function getCountOperationColumn(): GenericIndexPatternColumn { operationType: 'count', }; } -function getLayer(col1: GenericIndexPatternColumn = getStringBasedOperationColumn()) { +function getLayer( + col1: GenericIndexPatternColumn = getStringBasedOperationColumn(), + indexPattern?: IndexPattern +) { return { - indexPatternId: '1', + indexPatternId: defaultProps.indexPattern.id, columnOrder: ['col1', 'col2'], columns: { col1, @@ -112,7 +119,11 @@ function getLayer(col1: GenericIndexPatternColumn = getStringBasedOperationColum }, }; } -function getDefaultOperationSupportMatrix(layer: FormBasedLayer, columnId: string) { +function getDefaultOperationSupportMatrix( + layer: FormBasedLayer, + columnId: string, + indexPattern?: IndexPattern +) { return getOperationSupportMatrix({ state: { layers: { layer1: layer }, @@ -121,7 +132,7 @@ function getDefaultOperationSupportMatrix(layer: FormBasedLayer, columnId: strin filterOperations: () => true, columnId, indexPatterns: { - [defaultProps.indexPattern.id]: defaultProps.indexPattern, + [defaultProps.indexPattern.id]: indexPattern ?? defaultProps.indexPattern, }, }); } @@ -421,6 +432,80 @@ describe('FieldInput', () => { expect(onDeleteColumn).toHaveBeenCalled(); expect(updateLayerSpy).not.toHaveBeenCalled(); }); + + describe('time series group', () => { + function getLayerWithTSDBMetric() { + const layer = getLayer(); + layer.columns.col2 = { + label: 'Min of TSDB counter', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes_counter', + operationType: 'min', + }; + return layer; + } + it('should not render the time dimension category if it has tsdb metric column but the group is not a breakdown', () => { + const updateLayerSpy = jest.fn(); + const layer = getLayerWithTSDBMetric(); + const operationSupportMatrix = getDefaultOperationSupportMatrix(layer, 'col1'); + const instance = mount( + + ); + + expect(instance.find(FieldSelect).prop('showTimeSeriesDimensions')).toBeFalsy(); + }); + + it('should render the time dimension category if it has tsdb metric column and the group is a breakdown one', () => { + const updateLayerSpy = jest.fn(); + const layer = getLayerWithTSDBMetric(); + const operationSupportMatrix = getDefaultOperationSupportMatrix(layer, 'col1'); + const instance = mount( + + ); + + expect(instance.find(FieldSelect).prop('showTimeSeriesDimensions')).toBeTruthy(); + }); + }); }); describe('getErrorMessage', () => { diff --git a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.tsx b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.tsx index 462cd0b546f22..5120c89c37b30 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_input.tsx @@ -16,6 +16,7 @@ import type { GenericIndexPatternColumn, } from '../operations/definitions'; import type { FieldBasedIndexPatternColumn } from '../operations/definitions/column_types'; +import { shouldShowTimeSeriesOption } from '../pure_utils'; export function FieldInput({ layer, @@ -83,6 +84,12 @@ export function FieldInput({ }) ); }} + showTimeSeriesDimensions={shouldShowTimeSeriesOption( + layer, + indexPattern, + groupId, + dimensionGroups + )} /> ); diff --git a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_select.tsx index cbfafbfbfd2c9..909bc64781399 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/field_select.tsx @@ -33,6 +33,7 @@ export interface FieldSelectProps extends EuiComboBoxProps { @@ -66,7 +68,15 @@ export function FieldSelect({ return fieldContainsData(fieldName, currentIndexPattern, hasFieldData); } - function fieldNamesToOptions(items: string[]) { + interface FieldOption { + label: string; + value: { type: 'field'; field: string; dataType: string | undefined; operationType: string }; + exists: boolean; + compatible: number; + 'data-test-subj': string; + } + + function fieldNamesToOptions(items: string[]): FieldOption[] { return items .filter((field) => currentIndexPattern.getFieldByName(field)?.displayName) .map((field) => { @@ -75,9 +85,9 @@ export function FieldSelect({ const exists = containsData(field); const fieldInstance = currentIndexPattern.getFieldByName(field); return { - label: currentIndexPattern.getFieldByName(field)?.displayName, + label: currentIndexPattern.getFieldByName(field)?.displayName ?? field, value: { - type: 'field', + type: 'field' as const, field, dataType: fieldInstance ? getFieldType(fieldInstance) : undefined, // Use the operation directly, or choose the first compatible operation. @@ -102,21 +112,47 @@ export function FieldSelect({ ); const [availableFields, emptyFields] = partition(nonMetaFields, containsData); - const constructFieldsOptions = (fieldsArr: string[], label: string) => + const constructFieldsOptions = ( + fieldsArr: string[], + label: string + ): { label: string; options: FieldOption[] } | false => fieldsArr.length > 0 && { label, options: fieldNamesToOptions(fieldsArr), }; - const availableFieldsOptions = constructFieldsOptions( + const isTimeSeriesFields = (field: string) => { + return ( + showTimeSeriesDimensions && currentIndexPattern.getFieldByName(field)?.timeSeriesDimension + ); + }; + + const [availableTimeSeriesFields, availableNonTimeseriesFields] = partition( availableFields, + isTimeSeriesFields + ); + const [emptyTimeSeriesFields, emptyNonTimeseriesFields] = partition( + emptyFields, + isTimeSeriesFields + ); + + const timeSeriesFieldsOptions = constructFieldsOptions( + // This group includes both available and empty fields + availableTimeSeriesFields.concat(emptyTimeSeriesFields), + i18n.translate('xpack.lens.indexPattern.timeSeriesFieldsLabel', { + defaultMessage: 'Time series dimensions', + }) + ); + + const availableFieldsOptions = constructFieldsOptions( + availableNonTimeseriesFields, i18n.translate('xpack.lens.indexPattern.availableFieldsLabel', { defaultMessage: 'Available fields', }) ); const emptyFieldsOptions = constructFieldsOptions( - emptyFields, + emptyNonTimeseriesFields, i18n.translate('xpack.lens.indexPattern.emptyFieldsLabel', { defaultMessage: 'Empty fields', }) @@ -131,17 +167,19 @@ export function FieldSelect({ return [ ...fieldNamesToOptions(specialFields), + timeSeriesFieldsOptions, availableFieldsOptions, emptyFieldsOptions, metaFieldsOptions, ].filter(Boolean); }, [ + operationByField, incompleteOperation, selectedOperationType, currentIndexPattern, - operationByField, hasFieldData, markAllFieldsCompatible, + showTimeSeriesDimensions, ]); return ( diff --git a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx index 28810825dd7a0..66a01daae0581 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx @@ -317,6 +317,7 @@ export const ReferenceEditor = (props: ReferenceEditorProps) => { markAllFieldsCompatible={selectionStyle === 'field'} onDeleteColumn={onDeleteColumn} onChoose={onChooseField} + showTimeSeriesDimensions={false} /> ) : null} diff --git a/x-pack/plugins/lens/public/datasources/form_based/mocks.ts b/x-pack/plugins/lens/public/datasources/form_based/mocks.ts index c01495f1993e4..fcefa97ecd4b1 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/mocks.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/mocks.ts @@ -216,3 +216,15 @@ export const createMockedIndexPatternWithoutType = ( getFieldByName: getFieldByNameFactory(filteredFields), }; }; + +export const createMockedIndexPatternWithAdditionalFields = ( + newFields: IndexPatternField[] +): IndexPattern => { + const { fields, ...otherIndexPatternProps } = createMockedIndexPattern(); + const completeFields = fields.concat(newFields); + return { + ...otherIndexPatternProps, + fields: completeFields, + getFieldByName: getFieldByNameFactory(completeFields), + }; +}; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/field_inputs.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/field_inputs.tsx index 72941739b24ed..428747b6cda75 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/field_inputs.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/field_inputs.tsx @@ -30,6 +30,7 @@ export interface FieldInputsProps { invalidFields?: string[]; operationSupportMatrix: Pick; onChange: (newValues: string[]) => void; + showTimeSeriesDimensions: boolean; } interface WrappedValue { @@ -50,6 +51,7 @@ export function FieldInputs({ indexPattern, operationSupportMatrix, invalidFields, + showTimeSeriesDimensions, }: FieldInputsProps) { const onChangeWrapped = useCallback( (values: WrappedValue[]) => @@ -165,6 +167,7 @@ export function FieldInputs({ data-test-subj={ localValues.length !== 1 ? `indexPattern-dimension-field-${index}` : undefined } + showTimeSeriesDimensions={localValues.length < 2 && showTimeSeriesDimensions} /> ); diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/index.tsx index 504abd15955e0..4aafe38ea39ee 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/index.tsx @@ -53,6 +53,7 @@ import { supportedTypes, } from './constants'; import { IncludeExcludeRow } from './include_exclude_options'; +import { shouldShowTimeSeriesOption } from '../../../pure_utils'; export function supportsRarityRanking(field?: IndexPatternField) { // these es field types can't be sorted by rarity @@ -591,6 +592,12 @@ export const termsOperation: OperationDefinition< operationSupportMatrix={operationSupportMatrix} onChange={onFieldSelectChange} invalidFields={invalidFields} + showTimeSeriesDimensions={shouldShowTimeSeriesOption( + layer, + indexPattern, + groupId, + dimensionGroups + )} /> ); diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/values_input.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/values_input.tsx index 33dd5b7206008..a776227986d68 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/values_input.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/values_input.tsx @@ -94,6 +94,7 @@ export const ValuesInput = ({ const inputNumber = Number(inputValue); setInputValue(String(Math.min(maxValue, Math.max(inputNumber, minValue)))); }} + data-test-subj={'indexPattern-terms-values'} /> ); diff --git a/x-pack/plugins/lens/public/datasources/form_based/pure_utils.ts b/x-pack/plugins/lens/public/datasources/form_based/pure_utils.ts index b13fd50a45e73..256c1de51f832 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/pure_utils.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/pure_utils.ts @@ -5,7 +5,12 @@ * 2.0. */ -import type { DataType, IndexPattern, IndexPatternField } from '../../types'; +import type { + DataType, + IndexPattern, + IndexPatternField, + VisualizationDimensionGroupConfig, +} from '../../types'; import type { FormBasedLayer } from './types'; import type { BaseIndexPatternColumn, @@ -26,6 +31,28 @@ export function hasField(column: BaseIndexPatternColumn): column is FieldBasedIn return 'sourceField' in column; } +export function shouldShowTimeSeriesOption( + layer: FormBasedLayer, + indexPattern: IndexPattern, + groupId: string, + dimensionGroups: VisualizationDimensionGroupConfig[] +) { + return Boolean( + dimensionGroups.find(({ groupId: id }) => groupId === id)?.isBreakdownDimension && + containsColumnWithTimeSeriesMetric(layer, indexPattern) + ); +} + +function containsColumnWithTimeSeriesMetric( + layer: FormBasedLayer, + indexPattern: IndexPattern +): boolean { + return Object.values(layer.columns).some( + (column) => + hasField(column) && indexPattern.getFieldByName(column.sourceField)?.timeSeriesMetric + ); +} + export function getFieldType(field: IndexPatternField) { if (field.timeSeriesMetric) { return field.timeSeriesMetric; diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts index 33010858162a5..0b0437cc96c0b 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts @@ -365,10 +365,26 @@ describe('Textbased Data Source', () => { describe('#getDatasourceSuggestionsForVisualizeField', () => { (generateId as jest.Mock).mockReturnValue(`newid`); it('should create the correct layers', () => { + const textBasedQueryColumns = [ + { + id: 'bytes', + name: 'bytes', + meta: { + type: 'number', + }, + }, + { + id: 'dest', + name: 'dest', + meta: { + type: 'string', + }, + }, + ]; const state = { layers: {}, initialContext: { - contextualFields: ['bytes', 'dest'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT * FROM "foo"' }, dataViewSpec: { title: 'foo', @@ -385,34 +401,19 @@ describe('Textbased Data Source', () => { ); expect(suggestions[0].state).toEqual({ ...state, - fieldList: [ - { - id: 'newid', - meta: { - type: 'number', - }, - name: 'bytes', - }, - { - id: 'newid', - meta: { - type: 'string', - }, - name: 'dest', - }, - ], + fieldList: textBasedQueryColumns, layers: { newid: { allColumns: [ { - columnId: 'newid', + columnId: 'bytes', fieldName: 'bytes', meta: { type: 'number', }, }, { - columnId: 'newid', + columnId: 'dest', fieldName: 'dest', meta: { type: 'string', @@ -421,14 +422,14 @@ describe('Textbased Data Source', () => { ], columns: [ { - columnId: 'newid', + columnId: 'bytes', fieldName: 'bytes', meta: { type: 'number', }, }, { - columnId: 'newid', + columnId: 'dest', fieldName: 'dest', meta: { type: 'string', @@ -447,7 +448,7 @@ describe('Textbased Data Source', () => { changeType: 'initial', columns: [ { - columnId: 'newid', + columnId: 'bytes', operation: { dataType: 'number', isBucketed: false, @@ -455,7 +456,7 @@ describe('Textbased Data Source', () => { }, }, { - columnId: 'newid', + columnId: 'dest', operation: { dataType: 'string', isBucketed: true, @@ -472,7 +473,22 @@ describe('Textbased Data Source', () => { const state = { layers: {}, initialContext: { - contextualFields: ['bytes', 'dest'], + textBasedColumns: [ + { + id: 'bytes', + name: 'bytes', + meta: { + type: 'number', + }, + }, + { + id: 'dest', + name: 'dest', + meta: { + type: 'string', + }, + }, + ], dataViewSpec: { title: 'foo', id: '1', diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index c490c00b41f44..e2dc541e77e2d 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -13,7 +13,7 @@ import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { AggregateQuery } from '@kbn/es-query'; import type { SavedObjectReference } from '@kbn/core/public'; import { EuiFormRow } from '@elastic/eui'; -import type { ExpressionsStart, DatatableColumnType } from '@kbn/expressions-plugin/public'; +import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import type { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { euiThemeVars } from '@kbn/ui-theme'; @@ -41,7 +41,7 @@ import type { TextBasedField, } from './types'; import { FieldSelect } from './field_select'; -import type { Datasource, IndexPatternMap } from '../../types'; +import type { Datasource } from '../../types'; import { LayerPanel } from './layerpanel'; import { getUniqueLabelGenerator, nonNullable } from '../../utils'; import { onDrop, getDropProps } from './dnd'; @@ -105,30 +105,19 @@ export function getTextBasedDatasource({ const getSuggestionsForVisualizeField = ( state: TextBasedPrivateState, indexPatternId: string, - fieldName: string, - indexPatterns: IndexPatternMap + fieldName: string ) => { const context = state.initialContext; // on text based mode we offer suggestions for the query and not for a specific field if (fieldName) return []; if (context && 'dataViewSpec' in context && context.dataViewSpec.title && context.query) { const newLayerId = generateId(); - const indexPattern = indexPatterns[indexPatternId]; - - const contextualFields = context.contextualFields; - const newColumns = contextualFields?.map((c) => { - let field = indexPattern?.getFieldByName(c); - if (!field) { - field = indexPattern?.fields.find((f) => f.name.includes(c)); - } - const newId = generateId(); - const type = field?.type ?? 'number'; + const textBasedQueryColumns = context.textBasedColumns ?? []; + const newColumns = textBasedQueryColumns.map((c) => { return { - columnId: newId, - fieldName: c, - meta: { - type: type as DatatableColumnType, - }, + columnId: c.id, + fieldName: c.name, + meta: c.meta, }; }); @@ -136,14 +125,7 @@ export function getTextBasedDatasource({ const query = context.query; const updatedState = { ...state, - fieldList: - newColumns?.map((c) => { - return { - id: c.columnId, - name: c.fieldName, - meta: c.meta, - }; - }) ?? [], + fieldList: textBasedQueryColumns, layers: { ...state.layers, [newLayerId]: { diff --git a/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts b/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts index c15bf41b6fb8d..f8c2901b5c230 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts @@ -144,6 +144,22 @@ describe('Text based languages utils', () => { }); describe('getStateFromAggregateQuery', () => { + const textBasedQueryColumns = [ + { + id: 'bytes', + name: 'bytes', + meta: { + type: 'number', + }, + }, + { + id: 'dest', + name: 'dest', + meta: { + type: 'string', + }, + }, + ] as DatatableColumn[]; it('should return the correct state', async () => { const state = { layers: { @@ -157,7 +173,7 @@ describe('Text based languages utils', () => { indexPatternRefs: [], fieldList: [], initialContext: { - contextualFields: ['bytes', 'dest'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT * FROM "foo"' }, fieldName: '', dataViewSpec: { @@ -205,7 +221,7 @@ describe('Text based languages utils', () => { expect(updatedState).toStrictEqual({ initialContext: { - contextualFields: ['bytes', 'dest'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT * FROM "foo"' }, fieldName: '', dataViewSpec: { @@ -309,7 +325,7 @@ describe('Text based languages utils', () => { indexPatternRefs: [], fieldList: [], initialContext: { - contextualFields: ['bytes', 'dest'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT * FROM "foo"' }, fieldName: '', dataViewSpec: { @@ -362,7 +378,7 @@ describe('Text based languages utils', () => { expect(updatedState).toStrictEqual({ initialContext: { - contextualFields: ['bytes', 'dest'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT * FROM "foo"' }, fieldName: '', dataViewSpec: { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx index 18d0833ed4250..6514e13d65a11 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx @@ -530,7 +530,9 @@ describe('workspace_panel', () => { const onEvent = expressionRendererMock.mock.calls[0][0].onEvent!; - const eventData = { myData: true, table: { rows: [], columns: [] }, column: 0 }; + const eventData = { + data: [{ table: { rows: [], columns: [] }, cells: [{ column: 0, row: 0 }] }], + }; onEvent({ name: 'multiFilter', data: eventData }); expect(uiActionsMock.getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.multiFilter); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 3f92236c99e5d..02db9e18919f7 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -434,7 +434,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ data: { ...event.data, - timeFieldName: inferTimeField(plugins.data.datatableUtilities, event.data), + timeFieldName: inferTimeField(plugins.data.datatableUtilities, event), }, }); } @@ -442,7 +442,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ data: { ...event.data, - timeFieldName: inferTimeField(plugins.data.datatableUtilities, event.data), + timeFieldName: inferTimeField(plugins.data.datatableUtilities, event), }, }); } diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 8741d73a4a7b4..67dea2f98231c 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -1227,45 +1227,35 @@ export class Embeddable if (!this.deps.getTrigger || this.input.disableTriggers) { return; } + + let eventHandler: + | LensBaseEmbeddableInput['onBrushEnd'] + | LensBaseEmbeddableInput['onFilter'] + | LensBaseEmbeddableInput['onTableRowClick']; + let shouldExecuteDefaultTriggers = true; + if (isLensBrushEvent(event)) { - let shouldExecuteDefaultTriggers = true; - if (this.input.onBrushEnd) { - this.input.onBrushEnd({ - ...event.data, - preventDefault: () => { - shouldExecuteDefaultTriggers = false; - }, - }); - } - if (shouldExecuteDefaultTriggers) { - this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ - data: { - ...event.data, - timeFieldName: - event.data.timeFieldName || - inferTimeField(this.deps.data.datatableUtilities, event.data), - }, - embeddable: this, - }); - } + eventHandler = this.input.onBrushEnd; + } else if (isLensFilterEvent(event) || isLensMultiFilterEvent(event)) { + eventHandler = this.input.onFilter; + } else if (isLensTableRowContextMenuClickEvent(event)) { + eventHandler = this.input.onTableRowClick; } - if (isLensFilterEvent(event) || isLensMultiFilterEvent(event)) { - let shouldExecuteDefaultTriggers = true; - if (this.input.onFilter) { - this.input.onFilter({ - ...event.data, - preventDefault: () => { - shouldExecuteDefaultTriggers = false; - }, - }); - } + + eventHandler?.({ + ...event.data, + preventDefault: () => { + shouldExecuteDefaultTriggers = false; + }, + }); + + if (isLensFilterEvent(event) || isLensMultiFilterEvent(event) || isLensBrushEvent(event)) { if (shouldExecuteDefaultTriggers) { this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ data: { ...event.data, timeFieldName: - event.data.timeFieldName || - inferTimeField(this.deps.data.datatableUtilities, event.data), + event.data.timeFieldName || inferTimeField(this.deps.data.datatableUtilities, event), }, embeddable: this, }); @@ -1273,15 +1263,6 @@ export class Embeddable } if (isLensTableRowContextMenuClickEvent(event)) { - let shouldExecuteDefaultTriggers = true; - if (this.input.onTableRowClick) { - this.input.onTableRowClick({ - ...event.data, - preventDefault: () => { - shouldExecuteDefaultTriggers = false; - }, - }); - } if (shouldExecuteDefaultTriggers) { this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec( { diff --git a/x-pack/plugins/lens/public/lens_suggestions_api.test.ts b/x-pack/plugins/lens/public/lens_suggestions_api.test.ts index 4f2155a42a8f1..82271e56aa98a 100644 --- a/x-pack/plugins/lens/public/lens_suggestions_api.test.ts +++ b/x-pack/plugins/lens/public/lens_suggestions_api.test.ts @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -// import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import type { DataView } from '@kbn/data-views-plugin/public'; +import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { createMockVisualization, DatasourceMock, createMockDatasource } from './mocks'; import { DatasourceSuggestion } from './types'; import { suggestionsApi } from './lens_suggestions_api'; @@ -21,6 +21,23 @@ const generateSuggestion = (state = {}, layerId: string = 'first'): DatasourceSu keptLayerIds: [layerId], }); +const textBasedQueryColumns = [ + { + id: 'field1', + name: 'field1', + meta: { + type: 'number', + }, + }, + { + id: 'field2', + name: 'field2', + meta: { + type: 'string', + }, + }, +] as DatatableColumn[]; + describe('suggestionsApi', () => { let datasourceMap: Record; const mockVis = createMockVisualization(); @@ -46,7 +63,7 @@ describe('suggestionsApi', () => { name: 'DataView', }, fieldName: '', - contextualFields: ['field1', 'field2'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT field1, field2 FROM "index1"', }, @@ -93,7 +110,7 @@ describe('suggestionsApi', () => { name: 'DataView', }, fieldName: '', - contextualFields: ['field1', 'field2'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT field1, field2 FROM "index1"', }, @@ -142,7 +159,7 @@ describe('suggestionsApi', () => { name: 'DataView', }, fieldName: '', - contextualFields: ['field1', 'field2'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT field1, field2 FROM "index1"', }, @@ -184,7 +201,7 @@ describe('suggestionsApi', () => { name: 'DataView', }, fieldName: '', - contextualFields: ['field1', 'field2'], + textBasedColumns: textBasedQueryColumns, query: { sql: 'SELECT field1, field2 FROM "index1"', }, diff --git a/x-pack/plugins/lens/public/shared_components/legend/action/legend_action_popover.tsx b/x-pack/plugins/lens/public/shared_components/legend/action/legend_action_popover.tsx index 7c565cc540402..800d9b3bd6c05 100644 --- a/x-pack/plugins/lens/public/shared_components/legend/action/legend_action_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend/action/legend_action_popover.tsx @@ -40,7 +40,7 @@ export const LegendActionPopover: React.FunctionComponent, @@ -51,7 +51,7 @@ export const LegendActionPopover: React.FunctionComponent, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 3b989366fa307..13651c3bc5e69 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -814,6 +814,7 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { supportStaticValue?: boolean; // used by text based datasource to restrict the field selection only to number fields for the metric dimensions isMetricDimension?: boolean; + isBreakdownDimension?: boolean; paramEditorCustomProps?: ParamEditorCustomProps; enableFormatSelector?: boolean; labels?: { buttonAriaLabel: string; buttonLabel: string }; @@ -1331,6 +1332,12 @@ export interface LensTableRowContextMenuEvent { data: RowClickContext['data']; } +export type TriggerEvent = + | BrushTriggerEvent + | ClickTriggerEvent + | MultiClickTriggerEvent + | LensTableRowContextMenuEvent; + export function isLensFilterEvent(event: ExpressionRendererEvent): event is ClickTriggerEvent { return event.name === 'filter'; } diff --git a/x-pack/plugins/lens/public/utils.test.ts b/x-pack/plugins/lens/public/utils.test.ts index 52e509557b2fc..e775059586aff 100644 --- a/x-pack/plugins/lens/public/utils.test.ts +++ b/x-pack/plugins/lens/public/utils.test.ts @@ -56,9 +56,12 @@ describe('utils', () => { test('infer time field for brush event', () => { expect( inferTimeField(datatableUtilities, { - table, - column: 0, - range: [1, 2], + name: 'brush', + data: { + table, + column: 0, + range: [1, 2], + }, }) ).toEqual('abc'); }); @@ -66,9 +69,12 @@ describe('utils', () => { test('do not return time field if time range is not bound', () => { expect( inferTimeField(datatableUtilities, { - table: tableWithoutAppliedTimeRange, - column: 0, - range: [1, 2], + name: 'brush', + data: { + table: tableWithoutAppliedTimeRange, + column: 0, + range: [1, 2], + }, }) ).toEqual(undefined); }); @@ -76,14 +82,17 @@ describe('utils', () => { test('infer time field for click event', () => { expect( inferTimeField(datatableUtilities, { - data: [ - { - table, - column: 0, - row: 0, - value: 1, - }, - ], + name: 'filter', + data: { + data: [ + { + table, + column: 0, + row: 0, + value: 1, + }, + ], + }, }) ).toEqual('abc'); }); @@ -91,15 +100,18 @@ describe('utils', () => { test('do not return time field for negated click event', () => { expect( inferTimeField(datatableUtilities, { - data: [ - { - table, - column: 0, - row: 0, - value: 1, - }, - ], - negate: true, + name: 'filter', + data: { + data: [ + { + table, + column: 0, + row: 0, + value: 1, + }, + ], + negate: true, + }, }) ).toEqual(undefined); }); @@ -107,14 +119,17 @@ describe('utils', () => { test('do not return time field for click event without bound time field', () => { expect( inferTimeField(datatableUtilities, { - data: [ - { - table: tableWithoutAppliedTimeRange, - column: 0, - row: 0, - value: 1, - }, - ], + name: 'filter', + data: { + data: [ + { + table: tableWithoutAppliedTimeRange, + column: 0, + row: 0, + value: 1, + }, + ], + }, }) ).toEqual(undefined); }); diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index 446d7aac37e66..b1deface2cd77 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -14,11 +14,6 @@ import type { TimefilterContract } from '@kbn/data-plugin/public'; import type { IUiSettingsClient, SavedObjectReference } from '@kbn/core/public'; import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import { - BrushTriggerEvent, - ClickTriggerEvent, - MultiClickTriggerEvent, -} from '@kbn/charts-plugin/public'; import { emptyTitleText } from '@kbn/visualization-ui-components'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { ISearchStart } from '@kbn/data-plugin/public'; @@ -34,6 +29,10 @@ import { DragDropOperation, isOperation, UserMessage, + TriggerEvent, + isLensBrushEvent, + isLensMultiFilterEvent, + isLensFilterEvent, } from './types'; import type { DatasourceStates, VisualizationState } from './state_management'; import type { IndexPatternServiceAPI } from './data_views_service/service'; @@ -214,17 +213,28 @@ export function getRemoveOperation( return layerCount === 1 ? 'clear' : 'remove'; } -export function inferTimeField( - datatableUtilities: DatatableUtilitiesService, - context: BrushTriggerEvent['data'] | ClickTriggerEvent['data'] | MultiClickTriggerEvent['data'] -) { - const tablesAndColumns = - 'table' in context - ? [{ table: context.table, column: context.column }] - : !context.negate - ? context.data - : // if it's a negated filter, never respect bound time field - []; +function getTablesAndColumnsFromContext(event: TriggerEvent) { + // if it's a negated filter, never respect bound time field + if ('negate' in event.data && event.data.negate) { + return []; + } + if (isLensBrushEvent(event)) { + return [{ table: event.data.table, column: event.data.column }]; + } + if (isLensMultiFilterEvent(event)) { + return event.data.data.map(({ table, cells }) => ({ + table, + column: cells[0].column, + })); + } + if (isLensFilterEvent(event)) { + return event.data.data; + } + return event.data; +} + +export function inferTimeField(datatableUtilities: DatatableUtilitiesService, event: TriggerEvent) { + const tablesAndColumns = getTablesAndColumnsFromContext(event); return !Array.isArray(tablesAndColumns) ? [tablesAndColumns] : tablesAndColumns diff --git a/x-pack/plugins/lens/public/visualizations/datatable/components/columns.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/columns.tsx index b7380693bf2bf..6b5ce5b8168b7 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/components/columns.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/columns.tsx @@ -91,13 +91,13 @@ export const createGridColumns = ( const filterForText = i18n.translate( 'xpack.lens.table.tableCellFilter.filterForValueText', { - defaultMessage: 'Filter for value', + defaultMessage: 'Filter for', } ); const filterForAriaLabel = i18n.translate( 'xpack.lens.table.tableCellFilter.filterForValueAriaLabel', { - defaultMessage: 'Filter for value: {cellContent}', + defaultMessage: 'Filter for: {cellContent}', values: { cellContent, }, @@ -129,13 +129,13 @@ export const createGridColumns = ( const filterOutText = i18n.translate( 'xpack.lens.table.tableCellFilter.filterOutValueText', { - defaultMessage: 'Filter out value', + defaultMessage: 'Filter out', } ); const filterOutAriaLabel = i18n.translate( 'xpack.lens.table.tableCellFilter.filterOutValueAriaLabel', { - defaultMessage: 'Filter out value: {cellContent}', + defaultMessage: 'Filter out: {cellContent}', values: { cellContent, }, @@ -230,7 +230,7 @@ export const createGridColumns = ( onClick: () => handleTransposedColumnClick(bucketValues, false), iconType: 'plusInCircle', label: i18n.translate('xpack.lens.table.columnFilter.filterForValueText', { - defaultMessage: 'Filter for column', + defaultMessage: 'Filter for', }), 'data-test-subj': 'lensDatatableHide', }); @@ -241,7 +241,7 @@ export const createGridColumns = ( onClick: () => handleTransposedColumnClick(bucketValues, true), iconType: 'minusInCircle', label: i18n.translate('xpack.lens.table.columnFilter.filterOutValueText', { - defaultMessage: 'Filter out column', + defaultMessage: 'Filter out', }), 'data-test-subj': 'lensDatatableHide', }); diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts index 596c334aeac9e..dc9202a4d7e92 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts @@ -150,6 +150,7 @@ describe('heatmap', () => { groupLabel: 'Vertical axis', accessors: [{ columnId: 'y-accessor' }], filterOperations: filterOperationsAxis, + isBreakdownDimension: true, supportsMoreColumns: false, requiredMinDimensionCount: 0, dataTestSubj: 'lnsHeatmap_yDimensionPanel', @@ -210,6 +211,7 @@ describe('heatmap', () => { accessors: [], filterOperations: filterOperationsAxis, supportsMoreColumns: true, + isBreakdownDimension: true, requiredMinDimensionCount: 0, dataTestSubj: 'lnsHeatmap_yDimensionPanel', }, @@ -267,6 +269,7 @@ describe('heatmap', () => { accessors: [{ columnId: 'y-accessor' }], filterOperations: filterOperationsAxis, supportsMoreColumns: false, + isBreakdownDimension: true, requiredMinDimensionCount: 0, dataTestSubj: 'lnsHeatmap_yDimensionPanel', }, diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx index 654d69284718c..5d19c402a27e2 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx @@ -197,6 +197,7 @@ export const getHeatmapVisualization = ({ filterOperations: filterOperationsAxis, supportsMoreColumns: !state.yAccessor, requiredMinDimensionCount: 0, + isBreakdownDimension: true, dataTestSubj: 'lnsHeatmap_yDimensionPanel', }, { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 0d000db6e5206..9f5f9755d1781 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -461,6 +461,7 @@ export const getXyVisualization = ({ requiredMinDimensionCount: dataLayer.seriesType.includes('percentage') && hasOnlyOneAccessor ? 1 : 0, enableDimensionEditor: true, + isBreakdownDimension: true, }, ], }; diff --git a/x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.test.ts index bb21053cfe9d8..305f6292deb28 100644 --- a/x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.test.ts +++ b/x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.test.ts @@ -298,6 +298,7 @@ const createSearchStrategyDependenciesMock = (): SearchStrategyDependencies => ( savedObjectsClient: savedObjectsClientMock.create(), searchSessionsClient: createSearchSessionsClientMock(), request: httpServerMock.createKibanaRequest(), + rollupsEnabled: true, }); // using the official data mock from within x-pack doesn't type-check successfully, diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/transform_request.ts b/x-pack/plugins/maps/public/connected_components/mb_map/transform_request.ts index 47f14956d6dca..c30fc533a322f 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/transform_request.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/transform_request.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; import { FONTS_API_PATH, MVT_GETTILE_API_PATH, @@ -22,7 +25,10 @@ export function transformRequest(url: string, resourceType: string | undefined) return { url, method: 'GET' as 'GET', - headers: { [ELASTIC_HTTP_VERSION_HEADER]: '1' }, + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '1', + [X_ELASTIC_INTERNAL_ORIGIN_REQUEST]: 'kibana', + }, }; } @@ -30,7 +36,10 @@ export function transformRequest(url: string, resourceType: string | undefined) return { url, method: 'GET' as 'GET', - headers: { [ELASTIC_HTTP_VERSION_HEADER]: '1' }, + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '1', + [X_ELASTIC_INTERNAL_ORIGIN_REQUEST]: 'kibana', + }, }; } @@ -38,7 +47,10 @@ export function transformRequest(url: string, resourceType: string | undefined) return { url, method: 'GET' as 'GET', - headers: { [ELASTIC_HTTP_VERSION_HEADER]: '1' }, + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '1', + [X_ELASTIC_INTERNAL_ORIGIN_REQUEST]: 'kibana', + }, }; } diff --git a/x-pack/plugins/ml/kibana.jsonc b/x-pack/plugins/ml/kibana.jsonc index 04ab462b564cd..e2b327009f66a 100644 --- a/x-pack/plugins/ml/kibana.jsonc +++ b/x-pack/plugins/ml/kibana.jsonc @@ -7,10 +7,7 @@ "id": "ml", "server": true, "browser": true, - "configPath": [ - "xpack", - "ml" - ], + "configPath": ["xpack", "ml"], "requiredPlugins": [ "aiops", "charts", @@ -30,7 +27,9 @@ "uiActions", "unifiedSearch", "savedObjectsManagement", - "savedSearch" + "savedSearch", + "contentManagement", + "presentationUtil" ], "optionalPlugins": [ "alerting", @@ -58,8 +57,6 @@ "usageCollection", "unifiedSearch" ], - "extraPublicDirs": [ - "common" - ] + "extraPublicDirs": ["common"] } } diff --git a/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx b/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx index 06c0a4398afbc..d5d2f24276cb6 100644 --- a/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx +++ b/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx @@ -60,6 +60,9 @@ export const ChangePointDetectionPage: FC = () => { 'unifiedSearch', 'theme', 'lens', + 'presentationUtil', + 'embeddable', + 'cases', ]), fieldStats: { useFieldStatsTrigger, FieldStatsFlyoutProvider }, }} diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 559ec601da4d7..9a1e7169bebe3 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -101,6 +101,8 @@ const App: FC = ({ coreStart, deps, appMountParams }) => { lens: deps.lens, savedObjectsManagement: deps.savedObjectsManagement, savedSearch: deps.savedSearch, + contentManagement: deps.contentManagement, + presentationUtil: deps.presentationUtil, ...coreStart, mlServices: getMlGlobalServices(coreStart.http, deps.usageCollection), }; diff --git a/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts b/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts index 72a6100c15a7c..c5325f4aa7614 100644 --- a/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts +++ b/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts @@ -24,7 +24,7 @@ import { type MlApiServices } from '../services/ml_api_service'; let _capabilities: MlCapabilities = getDefaultCapabilities(); -const CAPABILITIES_REFRESH_INTERVAL = 60000; +const CAPABILITIES_REFRESH_INTERVAL = 5 * 60 * 1000; // 5min; export class MlCapabilitiesService { private _isLoading$ = new BehaviorSubject(true); diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx index b14abfe2f9895..f72fb8d00f173 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx @@ -10,15 +10,36 @@ import type { DataView } from '@kbn/data-plugin/common'; import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; import type { FieldStatsProps } from '@kbn/unified-field-list/src/components/field_stats'; -import { MLFieldStatsFlyoutContext } from './use_field_stats_flytout_context'; +import { useEffect } from 'react'; +import { getProcessedFields } from '@kbn/ml-data-grid'; +import { stringHash } from '@kbn/ml-string-hash'; +import { lastValueFrom } from 'rxjs'; +import { useRef } from 'react'; +import { getMergedSampleDocsForPopulatedFieldsQuery } from './populated_fields/get_merged_populated_fields_query'; +import { useMlKibana } from '../../contexts/kibana'; import { FieldStatsFlyout } from './field_stats_flyout'; +import { MLFieldStatsFlyoutContext } from './use_field_stats_flytout_context'; +import { PopulatedFieldsCacheManager } from './populated_fields/populated_fields_cache_manager'; export const FieldStatsFlyoutProvider: FC<{ dataView: DataView; fieldStatsServices: FieldStatsServices; timeRangeMs?: TimeRangeMs; dslQuery?: FieldStatsProps['dslQuery']; -}> = ({ dataView, fieldStatsServices, timeRangeMs, dslQuery, children }) => { + disablePopulatedFields?: boolean; +}> = ({ + dataView, + fieldStatsServices, + timeRangeMs, + dslQuery, + disablePopulatedFields = false, + children, +}) => { + const { + services: { + data: { search }, + }, + } = useMlKibana(); const [isFieldStatsFlyoutVisible, setFieldStatsIsFlyoutVisible] = useState(false); const [fieldName, setFieldName] = useState(); const [fieldValue, setFieldValue] = useState(); @@ -27,6 +48,86 @@ export const FieldStatsFlyoutProvider: FC<{ () => setFieldStatsIsFlyoutVisible(!isFieldStatsFlyoutVisible), [isFieldStatsFlyoutVisible] ); + const [manager] = useState(new PopulatedFieldsCacheManager()); + const [populatedFields, setPopulatedFields] = useState | undefined>(); + const abortController = useRef(new AbortController()); + + useEffect( + function fetchSampleDocsEffect() { + if (disablePopulatedFields) return; + + let unmounted = false; + + if (abortController.current) { + abortController.current.abort(); + abortController.current = new AbortController(); + } + + const queryAndRunTimeMappings = getMergedSampleDocsForPopulatedFieldsQuery({ + searchQuery: dslQuery, + runtimeFields: dataView.getRuntimeMappings(), + datetimeField: dataView.getTimeField()?.name, + timeRange: timeRangeMs, + }); + const indexPattern = dataView.getIndexPattern(); + const esSearchRequestParams = { + index: indexPattern, + body: { + fields: ['*'], + _source: false, + ...queryAndRunTimeMappings, + size: 1000, + }, + }; + const cacheKey = stringHash(JSON.stringify(esSearchRequestParams)).toString(); + + const fetchSampleDocuments = async function () { + try { + const resp = await lastValueFrom( + search.search( + { + params: esSearchRequestParams, + }, + { abortSignal: abortController.current.signal } + ) + ); + + const docs = resp.rawResponse.hits.hits.map((d) => getProcessedFields(d.fields ?? {})); + + // Get all field names for each returned doc and flatten it + // to a list of unique field names used across all docs. + const fieldsWithData = new Set(docs.map(Object.keys).flat(1)); + manager.set(cacheKey, fieldsWithData); + if (!unmounted) { + setPopulatedFields(fieldsWithData); + } + } catch (e) { + if (e.name !== 'AbortError') { + // eslint-disable-next-line no-console + console.error( + `An error occurred fetching sample documents to determine populated field stats. + \nQuery:\n${JSON.stringify(esSearchRequestParams)} + \nError:${e}` + ); + } + } + }; + + const cachedResult = manager.get(cacheKey); + if (cachedResult) { + return cachedResult; + } else { + fetchSampleDocuments(); + } + + return () => { + unmounted = true; + abortController.current.abort(); + }; + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [JSON.stringify({ dslQuery, dataViewId: dataView.id, timeRangeMs })] + ); return ( ; export const FieldStatsInfoButton = ({ field, label, - searchValue = '', onButtonClick, + disabled, + isEmpty = false, + hideTrigger = false, }: { field: FieldForStats; label: string; searchValue?: string; + disabled?: boolean; + isEmpty?: boolean; onButtonClick?: (field: FieldForStats) => void; + hideTrigger?: boolean; }) => { + const themeVars = useCurrentThemeVars(); + const emptyFieldMessage = isEmpty + ? ' ' + + i18n.translate('xpack.ml.newJob.wizard.fieldContextPopover.emptyFieldInSampleDocsMsg', { + defaultMessage: '(no data found in 1000 sample records)', + }) + : ''; return ( - - ) => { - if (ev.type === 'click') { - ev.currentTarget.focus(); - } - ev.preventDefault(); - ev.stopPropagation(); + > + ) => { + if (ev.type === 'click') { + ev.currentTarget.focus(); + } + ev.preventDefault(); + ev.stopPropagation(); - if (onButtonClick) { - onButtonClick(field); - } - }} - aria-label={i18n.translate( - 'xpack.ml.newJob.wizard.fieldContextPopover.inspectFieldStatsTooltipArialabel', - { - defaultMessage: 'Inspect field statistics', + if (onButtonClick) { + onButtonClick(field); + } + }} + aria-label={ + i18n.translate( + 'xpack.ml.newJob.wizard.fieldContextPopover.inspectFieldStatsTooltipAriaLabel', + { + defaultMessage: 'Inspect field statistics', + } + ) + emptyFieldMessage } - )} - /> - + /> + + ) : null} - - + + - {label} + + {label} + ); diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/get_merged_populated_fields_query.test.ts b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/get_merged_populated_fields_query.test.ts new file mode 100644 index 0000000000000..0ead649ab428a --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/get_merged_populated_fields_query.test.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { getMergedSampleDocsForPopulatedFieldsQuery } from './get_merged_populated_fields_query'; + +describe('getMergedSampleDocsForPopulatedFieldsQuery()', () => { + it('should wrap the original query in function_score', () => { + expect( + getMergedSampleDocsForPopulatedFieldsQuery({ + searchQuery: { match_all: {} }, + runtimeFields: {}, + }) + ).toStrictEqual({ + query: { + function_score: { query: { bool: { must: [{ match_all: {} }] } }, random_score: {} }, + }, + runtime_mappings: {}, + }); + }); + + it('should append the time range to the query if timeRange and datetimeField are provided', () => { + expect( + getMergedSampleDocsForPopulatedFieldsQuery({ + searchQuery: { + bool: { + should: [{ match_phrase: { version: '1' } }], + minimum_should_match: 1, + filter: [ + { + terms: { + cluster_uuid: '', + }, + }, + ], + must_not: [], + }, + }, + runtimeFields: {}, + timeRange: { from: 1613995874349, to: 1614082617000 }, + datetimeField: '@timestamp', + }) + ).toStrictEqual({ + query: { + function_score: { + query: { + bool: { + filter: [ + { terms: { cluster_uuid: '' } }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1613995874349, + lte: 1614082617000, + }, + }, + }, + ], + minimum_should_match: 1, + must_not: [], + should: [{ match_phrase: { version: '1' } }], + }, + }, + random_score: {}, + }, + }, + runtime_mappings: {}, + }); + }); + + it('should not append the time range to the query if datetimeField is undefined', () => { + expect( + getMergedSampleDocsForPopulatedFieldsQuery({ + searchQuery: { + bool: { + should: [{ match_phrase: { airline: 'AAL' } }], + minimum_should_match: 1, + filter: [], + must_not: [], + }, + }, + runtimeFields: {}, + timeRange: { from: 1613995874349, to: 1614082617000 }, + }) + ).toStrictEqual({ + query: { + function_score: { + query: { + bool: { + filter: [], + minimum_should_match: 1, + must_not: [], + should: [{ match_phrase: { airline: 'AAL' } }], + }, + }, + random_score: {}, + }, + }, + runtime_mappings: {}, + }); + }); +}); diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/get_merged_populated_fields_query.ts b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/get_merged_populated_fields_query.ts new file mode 100644 index 0000000000000..7287392849980 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/get_merged_populated_fields_query.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { cloneDeep } from 'lodash'; +import { getDefaultDSLQuery } from '@kbn/ml-query-utils'; + +export const getMergedSampleDocsForPopulatedFieldsQuery = ({ + runtimeFields, + searchQuery, + datetimeField, + timeRange, +}: { + runtimeFields: estypes.MappingRuntimeFields; + searchQuery?: estypes.QueryDslQueryContainer; + datetimeField?: string; + timeRange?: TimeRangeMs; +}): { + query: estypes.QueryDslQueryContainer; + runtime_mappings?: estypes.MappingRuntimeFields; +} => { + let rangeFilter; + + if (timeRange && datetimeField !== undefined) { + if (isPopulatedObject(timeRange, ['from', 'to']) && timeRange.to > timeRange.from) { + rangeFilter = { + range: { + [datetimeField]: { + gte: timeRange.from, + lte: timeRange.to, + format: 'epoch_millis', + }, + }, + }; + } + } + + const query = cloneDeep( + !searchQuery || isPopulatedObject(searchQuery, ['match_all']) + ? getDefaultDSLQuery() + : searchQuery + ); + + if (rangeFilter && isPopulatedObject(query, ['bool'])) { + if (Array.isArray(query.bool.filter)) { + query.bool.filter.push(rangeFilter); + } else { + query.bool.filter = [rangeFilter]; + } + } + + const queryAndRuntimeFields: { + query: estypes.QueryDslQueryContainer; + runtime_mappings?: estypes.MappingRuntimeFields; + } = { + query: { + function_score: { + query, + // @ts-expect-error random_score is valid dsl query + random_score: {}, + }, + }, + }; + if (runtimeFields) { + queryAndRuntimeFields.runtime_mappings = runtimeFields; + } + return queryAndRuntimeFields; +}; diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/index.ts b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/index.ts new file mode 100644 index 0000000000000..339f112beeb9f --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/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 { PopulatedFieldsCacheManager } from './populated_fields_cache_manager'; diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/populated_fields_cache_manager.ts b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/populated_fields_cache_manager.ts new file mode 100644 index 0000000000000..547ad65c1179e --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/populated_fields/populated_fields_cache_manager.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. + */ + +type StringifiedQueryKey = string; +type UpdatedTimestamp = number; + +const DEFAULT_EXPIRATION_MS = 60000; +export class PopulatedFieldsCacheManager { + private _resultsCache = new Map(); + private readonly _lastUpdatedTimestamps = new Map(); + + constructor(private readonly _expirationDurationMs = DEFAULT_EXPIRATION_MS) {} + + private clearOldCacheIfNeeded() { + if (this._resultsCache.size > 10) { + this._resultsCache.clear(); + this._lastUpdatedTimestamps.clear(); + } + } + + private clearExpiredCache(key: StringifiedQueryKey) { + // If result is available but past the expiration duration, clear cache + const lastUpdatedTs = this._lastUpdatedTimestamps.get(key); + const now = Date.now(); + if (lastUpdatedTs !== undefined && lastUpdatedTs - now > this._expirationDurationMs) { + this._resultsCache.delete(key); + } + } + + public get(key: StringifiedQueryKey) { + return this._resultsCache.get(key); + } + + public set(key: StringifiedQueryKey, value: any) { + this.clearExpiredCache(key); + this.clearOldCacheIfNeeded(); + + this._resultsCache.set(key, Date.now()); + this._resultsCache.set(key, value); + } +} diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts b/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts index 2de9fda1ded17..8aa7cfcc42de4 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts @@ -6,6 +6,7 @@ */ import { createContext, useContext } from 'react'; +import { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; interface MLJobWizardFieldStatsFlyoutProps { isFlyoutVisible: boolean; setIsFlyoutVisible: (v: boolean) => void; @@ -14,6 +15,8 @@ interface MLJobWizardFieldStatsFlyoutProps { fieldName?: string; setFieldValue: (v: string) => void; fieldValue?: string | number; + timeRangeMs?: TimeRangeMs; + populatedFields?: Set; } export const MLFieldStatsFlyoutContext = createContext({ isFlyoutVisible: false, @@ -21,6 +24,8 @@ export const MLFieldStatsFlyoutContext = createContext {}, setFieldName: () => {}, setFieldValue: () => {}, + timeRangeMs: undefined, + populatedFields: undefined, }); export function useFieldStatsFlyoutContext() { diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_trigger.tsx b/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_trigger.tsx index da69ad87c46bc..52011e72a6ddc 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_trigger.tsx +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_trigger.tsx @@ -6,17 +6,17 @@ */ import React, { ReactNode, useCallback } from 'react'; -import { EuiComboBoxOptionOption } from '@elastic/eui'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; import type { Field } from '@kbn/ml-anomaly-utils'; import { optionCss } from './eui_combo_box_with_field_stats'; import { useFieldStatsFlyoutContext } from '.'; import { FieldForStats, FieldStatsInfoButton } from './field_stats_info_button'; - interface Option extends EuiComboBoxOptionOption { field: Field; } + export const useFieldStatsTrigger = () => { - const { setIsFlyoutVisible, setFieldName } = useFieldStatsFlyoutContext(); + const { setIsFlyoutVisible, setFieldName, populatedFields } = useFieldStatsFlyoutContext(); const closeFlyout = useCallback(() => setIsFlyoutVisible(false), [setIsFlyoutVisible]); @@ -29,6 +29,7 @@ export const useFieldStatsTrigger = () => { }, [setFieldName, setIsFlyoutVisible] ); + const renderOption = useCallback( (option: EuiComboBoxOptionOption, searchValue: string): ReactNode => { const field = (option as Option).field; @@ -36,13 +37,15 @@ export const useFieldStatsTrigger = () => { option.label ) : ( ); }, - [handleFieldStatsButtonClick] + // eslint-disable-next-line react-hooks/exhaustive-deps + [handleFieldStatsButtonClick, populatedFields?.size] ); return { renderOption, @@ -51,5 +54,6 @@ export const useFieldStatsTrigger = () => { handleFieldStatsButtonClick, closeFlyout, optionCss, + populatedFields, }; }; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/add_inference_pipeline_flyout.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/add_inference_pipeline_flyout.tsx new file mode 100644 index 0000000000000..7d4ea408111fe --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/add_inference_pipeline_flyout.tsx @@ -0,0 +1,186 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useMemo, useState } from 'react'; + +import { + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiFlyoutFooter, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; + +import { ModelItem } from '../../model_management/models_list'; +import type { AddInferencePipelineSteps } from './types'; +import { ADD_INFERENCE_PIPELINE_STEPS } from './constants'; +import { AddInferencePipelineFooter } from './components/add_inference_pipeline_footer'; +import { AddInferencePipelineHorizontalSteps } from './components/add_inference_pipeline_horizontal_steps'; +import { getInitialState, getModelType } from './state'; +import { PipelineDetails } from './components/pipeline_details'; +import { ProcessorConfiguration } from './components/processor_configuration'; +import { OnFailureConfiguration } from './components/on_failure_configuration'; +import { TestPipeline } from './components/test_pipeline'; +import { ReviewAndCreatePipeline } from './components/review_and_create_pipeline'; +import { useMlApiContext } from '../../contexts/kibana'; +import { getPipelineConfig } from './get_pipeline_config'; +import { validateInferencePipelineConfigurationStep } from './validation'; +import type { MlInferenceState, InferenceModelTypes } from './types'; +import { useFetchPipelines } from './hooks/use_fetch_pipelines'; + +export interface AddInferencePipelineFlyoutProps { + onClose: () => void; + model: ModelItem; +} + +export const AddInferencePipelineFlyout: FC = ({ + onClose, + model, +}) => { + // eslint-disable-next-line react-hooks/exhaustive-deps + const initialState = useMemo(() => getInitialState(model), [model.model_id]); + const [formState, setFormState] = useState(initialState); + const [step, setStep] = useState(ADD_INFERENCE_PIPELINE_STEPS.DETAILS); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + const { + trainedModels: { createInferencePipeline }, + } = useMlApiContext(); + + const modelType = getModelType(model); + + const createPipeline = async () => { + setFormState({ ...formState, creatingPipeline: true }); + try { + await createInferencePipeline(formState.pipelineName, getPipelineConfig(formState)); + setFormState({ + ...formState, + pipelineCreated: true, + creatingPipeline: false, + pipelineError: undefined, + }); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + const errorProperties = extractErrorProperties(e); + setFormState({ + ...formState, + creatingPipeline: false, + pipelineError: errorProperties.message ?? e.message, + }); + } + }; + + const pipelineNames = useFetchPipelines(); + + const handleConfigUpdate = (configUpdate: Partial) => { + setFormState({ ...formState, ...configUpdate }); + }; + + const { pipelineName: pipelineNameError, targetField: targetFieldError } = useMemo(() => { + const errors = validateInferencePipelineConfigurationStep( + formState.pipelineName, + pipelineNames + ); + return errors; + }, [pipelineNames, formState.pipelineName]); + + const sourceIndex = useMemo( + () => + Array.isArray(model.metadata?.analytics_config.source.index) + ? model.metadata?.analytics_config.source.index.join() + : model.metadata?.analytics_config.source.index, + // eslint-disable-next-line react-hooks/exhaustive-deps + [model?.model_id] + ); + + return ( + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.title', + { + defaultMessage: 'Deploy analytics model', + } + )} +

+
+
+ + + + {step === ADD_INFERENCE_PIPELINE_STEPS.DETAILS && ( + + )} + {step === ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR && model && ( + + )} + {step === ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE && ( + + )} + {step === ADD_INFERENCE_PIPELINE_STEPS.TEST && ( + + )} + {step === ADD_INFERENCE_PIPELINE_STEPS.CREATE && ( + + )} + + + + +
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_footer.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_footer.tsx new file mode 100644 index 0000000000000..f0a8beb2482f6 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_footer.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { AddInferencePipelineSteps } from '../types'; +import { + BACK_BUTTON_LABEL, + CANCEL_BUTTON_LABEL, + CLOSE_BUTTON_LABEL, + CONTINUE_BUTTON_LABEL, +} from '../constants'; +import { getSteps } from '../get_steps'; + +interface Props { + isDetailsStepValid: boolean; + isConfigureProcessorStepValid: boolean; + pipelineCreated: boolean; + creatingPipeline: boolean; + step: AddInferencePipelineSteps; + onClose: () => void; + onCreate: () => void; + setStep: React.Dispatch>; +} + +export const AddInferencePipelineFooter: FC = ({ + isDetailsStepValid, + isConfigureProcessorStepValid, + creatingPipeline, + pipelineCreated, + onClose, + onCreate, + step, + setStep, +}) => { + const { nextStep, previousStep, isContinueButtonEnabled } = useMemo( + () => getSteps(step, isDetailsStepValid, isConfigureProcessorStepValid), + [isDetailsStepValid, isConfigureProcessorStepValid, step] + ); + + return ( + + + + {pipelineCreated ? CLOSE_BUTTON_LABEL : CANCEL_BUTTON_LABEL} + + + + + {previousStep !== undefined && pipelineCreated === false ? ( + setStep(previousStep as AddInferencePipelineSteps)} + > + {BACK_BUTTON_LABEL} + + ) : null} + + + {nextStep !== undefined ? ( + setStep(nextStep as AddInferencePipelineSteps)} + disabled={!isContinueButtonEnabled} + fill + > + {CONTINUE_BUTTON_LABEL} + + ) : ( + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.addInferencePipelineModal.footer.create', + { + defaultMessage: 'Create pipeline', + } + )} + + )} + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_horizontal_steps.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_horizontal_steps.tsx new file mode 100644 index 0000000000000..9954ed8955259 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_horizontal_steps.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, memo } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiStepsHorizontal, EuiStepsHorizontalProps } from '@elastic/eui'; +import type { AddInferencePipelineSteps } from '../types'; +import { ADD_INFERENCE_PIPELINE_STEPS } from '../constants'; + +const steps = Object.values(ADD_INFERENCE_PIPELINE_STEPS); + +interface Props { + step: AddInferencePipelineSteps; + setStep: React.Dispatch>; + isDetailsStepValid: boolean; + isConfigureProcessorStepValid: boolean; +} + +export const AddInferencePipelineHorizontalSteps: FC = memo( + ({ step, setStep, isDetailsStepValid, isConfigureProcessorStepValid }) => { + const currentStepIndex = steps.findIndex((s) => s === step); + const navSteps: EuiStepsHorizontalProps['steps'] = [ + { + // Details + onClick: () => setStep(ADD_INFERENCE_PIPELINE_STEPS.DETAILS), + status: isDetailsStepValid ? 'complete' : 'incomplete', + title: i18n.translate( + 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.details.title', + { + defaultMessage: 'Details', + } + ), + }, + { + // Processor configuration + onClick: () => { + if (!isDetailsStepValid) return; + setStep(ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR); + }, + status: + isDetailsStepValid && isConfigureProcessorStepValid && currentStepIndex > 1 + ? 'complete' + : 'incomplete', + title: i18n.translate( + 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.configureProcessor.title', + { + defaultMessage: 'Configure processor', + } + ), + }, + { + // handle failures + onClick: () => { + if (!isDetailsStepValid) return; + setStep(ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE); + }, + status: currentStepIndex > 2 ? 'complete' : 'incomplete', + title: i18n.translate( + 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.handleFailures.title', + { + defaultMessage: 'Handle failures', + } + ), + }, + { + // Test + onClick: () => { + if (!isConfigureProcessorStepValid || !isDetailsStepValid) return; + setStep(ADD_INFERENCE_PIPELINE_STEPS.TEST); + }, + status: currentStepIndex > 3 ? 'complete' : 'incomplete', + title: i18n.translate( + 'xpack.ml.trainedModels.content.indices.transforms.addInferencePipelineModal.steps.test.title', + { + defaultMessage: 'Test (Optional)', + } + ), + }, + { + // Review and Create + onClick: () => { + if (!isConfigureProcessorStepValid) return; + setStep(ADD_INFERENCE_PIPELINE_STEPS.CREATE); + }, + status: isDetailsStepValid && isConfigureProcessorStepValid ? 'incomplete' : 'disabled', + title: i18n.translate( + 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.create.title', + { + defaultMessage: 'Create', + } + ), + }, + ]; + switch (step) { + case ADD_INFERENCE_PIPELINE_STEPS.DETAILS: + navSteps[0].status = 'current'; + break; + case ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR: + navSteps[1].status = 'current'; + break; + case ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE: + navSteps[2].status = 'current'; + break; + case ADD_INFERENCE_PIPELINE_STEPS.TEST: + navSteps[3].status = 'current'; + break; + case ADD_INFERENCE_PIPELINE_STEPS.CREATE: + navSteps[4].status = 'current'; + break; + } + return ; + } +); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/additional_advanced_settings.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/additional_advanced_settings.tsx new file mode 100644 index 0000000000000..4b3cfbfcf795b --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/additional_advanced_settings.tsx @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState, memo, useMemo } from 'react'; + +import { + EuiAccordion, + EuiFlexGroup, + EuiFieldText, + EuiFlexItem, + EuiFormRow, + EuiLink, + EuiPanel, + EuiTextArea, + htmlIdGenerator, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { AdditionalSettings, MlInferenceState } from '../types'; +import { SaveChangesButton } from './save_changes_button'; +import { useMlKibana } from '../../../contexts/kibana'; + +interface Props { + condition?: string; + tag?: string; + handleAdvancedConfigUpdate: (configUpdate: Partial) => void; +} + +export const AdditionalAdvancedSettings: FC = memo( + ({ handleAdvancedConfigUpdate, condition, tag }) => { + const [additionalSettings, setAdditionalSettings] = useState< + Partial | undefined + >(condition || tag ? { condition, tag } : undefined); + + const { + services: { + docLinks: { links }, + }, + } = useMlKibana(); + + const handleAdditionalSettingsChange = (settingsChange: Partial) => { + setAdditionalSettings({ ...additionalSettings, ...settingsChange }); + }; + + const accordionId = useMemo(() => htmlIdGenerator()(), []); + const additionalSettingsUpdated = useMemo( + () => additionalSettings?.tag !== tag || additionalSettings?.condition !== condition, + [additionalSettings, tag, condition] + ); + + const updateAdditionalSettings = () => { + handleAdvancedConfigUpdate({ ...additionalSettings }); + }; + + return ( + + + + + + {additionalSettingsUpdated ? ( + + ) : null} + + + } + > + + + {/* CONDITION */} + + + } + helpText={ + + Painless +
+ ), + }} + /> + } + > + ) => + handleAdditionalSettingsChange({ condition: e.target.value }) + } + /> + + + {/* TAG */} + + + + + } + helpText={ + + } + > + ) => + handleAdditionalSettingsChange({ tag: e.target.value }) + } + aria-label={i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.tagAriaLabel', + { defaultMessage: 'Optional tag identifier for the processor' } + )} + /> + + + + + + + + ); + } +); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/on_failure_configuration.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/on_failure_configuration.tsx new file mode 100644 index 0000000000000..bc8bc4eedb2d3 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/on_failure_configuration.tsx @@ -0,0 +1,270 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState, memo } from 'react'; + +import { + EuiButtonEmpty, + EuiCode, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiLink, + EuiSpacer, + EuiSwitch, + EuiSwitchEvent, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { SaveChangesButton } from './save_changes_button'; +import type { MlInferenceState } from '../types'; +import { getDefaultOnFailureConfiguration } from '../state'; +import { CANCEL_EDIT_MESSAGE, EDIT_MESSAGE } from '../constants'; +import { useMlKibana } from '../../../contexts/kibana'; +import { isValidJson } from '../../../../../common/util/validation_utils'; + +interface Props { + handleAdvancedConfigUpdate: (configUpdate: Partial) => void; + ignoreFailure: boolean; + onFailure: MlInferenceState['onFailure']; + takeActionOnFailure: MlInferenceState['takeActionOnFailure']; +} + +export const OnFailureConfiguration: FC = memo( + ({ handleAdvancedConfigUpdate, ignoreFailure, onFailure, takeActionOnFailure }) => { + const { + services: { + docLinks: { links }, + }, + } = useMlKibana(); + + const [editOnFailure, setEditOnFailure] = useState(false); + const [isOnFailureValid, setIsOnFailureValid] = useState(false); + const [onFailureString, setOnFailureString] = useState( + JSON.stringify(onFailure, null, 2) + ); + + const updateIgnoreFailure = (e: EuiSwitchEvent) => { + const checked = e.target.checked; + handleAdvancedConfigUpdate({ + ignoreFailure: checked, + ...(checked === true ? { takeActionOnFailure: false, onFailure: undefined } : {}), + }); + }; + + const updateOnFailure = () => { + handleAdvancedConfigUpdate({ onFailure: JSON.parse(onFailureString) }); + setEditOnFailure(false); + }; + + const handleOnFailureChange = (json: string) => { + setOnFailureString(json); + const valid = isValidJson(json); + setIsOnFailureValid(valid); + }; + + const handleTakeActionOnFailureChange = (checked: boolean) => { + handleAdvancedConfigUpdate({ + takeActionOnFailure: checked, + onFailure: checked === false ? undefined : getDefaultOnFailureConfiguration(), + }); + if (checked === false) { + setEditOnFailure(false); + setIsOnFailureValid(true); + } + }; + + const resetOnFailure = () => { + setOnFailureString(JSON.stringify(getDefaultOnFailureConfiguration(), null, 2)); + setIsOnFailureValid(true); + }; + + return ( + + + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.onFailureTitle', + { defaultMessage: 'Ingesting problematic documents' } + )} +

+
+ + +

+ +

+

+ {'ignore_failure'}, + inferenceDocsLink: ( + + Learn more. + + ), + }} + /> +

+

+ {'on_failure'}, + onFailureDocsLink: ( + + Learn more. + + ), + }} + /> +

+
+
+ + + + + + + + } + checked={ignoreFailure} + onChange={updateIgnoreFailure} + /> + + + + {ignoreFailure === false ? ( + + + + + } + checked={takeActionOnFailure} + onChange={(e: EuiSwitchEvent) => + handleTakeActionOnFailureChange(e.target.checked) + } + /> + + ) : null} + + + + + {takeActionOnFailure === true && ignoreFailure === false ? ( + + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.onFailureHeadingLabel', + { defaultMessage: 'Actions to take on failure' } + )} + + + } + labelAppend={ + + + { + setEditOnFailure(!editOnFailure); + }} + > + {editOnFailure ? CANCEL_EDIT_MESSAGE : EDIT_MESSAGE} + + + + {editOnFailure ? ( + + ) : null} + + + {editOnFailure ? ( + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.resetOnFailureButton', + { defaultMessage: 'Reset' } + )} + + ) : null} + + + } + helpText={ + + } + > + <> + {!editOnFailure ? ( + + {JSON.stringify(onFailure, null, 2)} + + ) : null} + {editOnFailure ? ( + + ) : null} + + + ) : null} + +
+
+
+ + + ); + } +); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/pipeline_details.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/pipeline_details.tsx new file mode 100644 index 0000000000000..4988c772c2863 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/pipeline_details.tsx @@ -0,0 +1,223 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, memo } from 'react'; + +import { + EuiCode, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiLink, + EuiSpacer, + EuiTitle, + EuiText, + EuiTextArea, + EuiPanel, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useMlKibana } from '../../../contexts/kibana'; +import type { MlInferenceState } from '../types'; + +interface Props { + handlePipelineConfigUpdate: (configUpdate: Partial) => void; + modelId: string; + pipelineNameError: string | undefined; + pipelineName: string; + pipelineDescription: string; + targetField: string; + targetFieldError: string | undefined; +} + +export const PipelineDetails: FC = memo( + ({ + handlePipelineConfigUpdate, + modelId, + pipelineName, + pipelineNameError, + pipelineDescription, + targetField, + targetFieldError, + }) => { + const { + services: { + docLinks: { links }, + }, + } = useMlKibana(); + + const handleConfigChange = (value: string, type: string) => { + handlePipelineConfigUpdate({ [type]: value }); + }; + + return ( + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.title', + { defaultMessage: 'Create a pipeline' } + )} +

+
+ + +

+ {modelId}, + pipeline: ( + + pipeline + + ), + }} + /> +

+

+ + _reindex API + + ), + pipelineSimulateLink: ( + + pipeline/_simulate + + ), + }} + /> +

+
+
+ + + {/* NAME */} + + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.name.helpText', + { + defaultMessage: + 'Pipeline names are unique within a deployment and can only contain letters, numbers, underscores, and hyphens.', + } + )} + + ) + } + error={pipelineNameError} + isInvalid={pipelineNameError !== undefined} + > + ) => + handleConfigChange(e.target.value, 'pipelineName') + } + /> + + {/* DESCRIPTION */} + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.description.helpText', + { + defaultMessage: 'A description of what this pipeline does.', + } + )} + + } + > + ) => + handleConfigChange(e.target.value, 'pipelineDescription') + } + /> + + {/* TARGET FIELD */} + {'ml.inference.'} }} + /> + ) + } + error={targetFieldError} + isInvalid={targetFieldError !== undefined} + > + ) => + handleConfigChange(e.target.value, 'targetField') + } + /> + + + + +
+ ); + } +); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/processor_configuration.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/processor_configuration.tsx new file mode 100644 index 0000000000000..7f2dfe9ede728 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/processor_configuration.tsx @@ -0,0 +1,391 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState, memo } from 'react'; + +import { + EuiButtonEmpty, + EuiCode, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, + EuiPopover, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { ModelItem } from '../../../model_management/models_list'; +import { + EDIT_MESSAGE, + CANCEL_EDIT_MESSAGE, + CREATE_FIELD_MAPPING_MESSAGE, + CLEAR_BUTTON_LABEL, +} from '../constants'; +import { validateInferenceConfig } from '../validation'; +import { isValidJson } from '../../../../../common/util/validation_utils'; +import { SaveChangesButton } from './save_changes_button'; +import { useMlKibana } from '../../../contexts/kibana'; +import type { MlInferenceState, InferenceModelTypes } from '../types'; +import { AdditionalAdvancedSettings } from './additional_advanced_settings'; +import { validateFieldMap } from '../validation'; + +function getDefaultFieldMapString() { + return JSON.stringify( + { + field_map: { + incoming_field: 'field_the_model_expects', + }, + }, + null, + 2 + ); +} + +interface Props { + condition?: string; + fieldMap: MlInferenceState['fieldMap']; + handleAdvancedConfigUpdate: (configUpdate: Partial) => void; + inferenceConfig: ModelItem['inference_config']; + modelInferenceConfig: ModelItem['inference_config']; + modelInputFields: ModelItem['input']; + modelType?: InferenceModelTypes; + setHasUnsavedChanges: React.Dispatch>; + tag?: string; +} + +export const ProcessorConfiguration: FC = memo( + ({ + condition, + fieldMap, + handleAdvancedConfigUpdate, + inferenceConfig, + modelInputFields, + modelInferenceConfig, + modelType, + setHasUnsavedChanges, + tag, + }) => { + const { + services: { + docLinks: { links }, + }, + } = useMlKibana(); + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [editInferenceConfig, setEditInferenceConfig] = useState(false); + const [editFieldMapping, setEditFieldMapping] = useState(false); + const [inferenceConfigString, setInferenceConfigString] = useState( + JSON.stringify(inferenceConfig, null, 2) + ); + const [inferenceConfigError, setInferenceConfigError] = useState(); + const [fieldMapError, setFieldMapError] = useState(); + const [fieldMappingString, setFieldMappingString] = useState( + fieldMap ? JSON.stringify(fieldMap, null, 2) : getDefaultFieldMapString() + ); + const [isInferenceConfigValid, setIsInferenceConfigValid] = useState(true); + const [isFieldMapValid, setIsFieldMapValid] = useState(true); + + const handleInferenceConfigChange = (json: string) => { + setInferenceConfigString(json); + const valid = isValidJson(json); + setIsInferenceConfigValid(valid); + }; + + const updateInferenceConfig = () => { + const invalidInferenceConfigMessage = validateInferenceConfig( + JSON.parse(inferenceConfigString), + modelType + ); + + if (invalidInferenceConfigMessage === undefined) { + handleAdvancedConfigUpdate({ inferenceConfig: JSON.parse(inferenceConfigString) }); + setHasUnsavedChanges(false); + setEditInferenceConfig(false); + setInferenceConfigError(undefined); + } else { + setHasUnsavedChanges(true); + setIsInferenceConfigValid(false); + setInferenceConfigError(invalidInferenceConfigMessage); + } + }; + + const resetInferenceConfig = () => { + setInferenceConfigString(JSON.stringify(modelInferenceConfig, null, 2)); + setIsInferenceConfigValid(true); + setInferenceConfigError(undefined); + }; + + const clearFieldMap = () => { + setFieldMappingString('{}'); + setIsFieldMapValid(true); + setFieldMapError(undefined); + }; + + const handleFieldMapChange = (json: string) => { + setFieldMappingString(json); + const valid = isValidJson(json); + setIsFieldMapValid(valid); + }; + + const updateFieldMap = () => { + const invalidFieldMapMessage = validateFieldMap( + modelInputFields.field_names ?? [], + JSON.parse(fieldMappingString) + ); + + if (invalidFieldMapMessage === undefined) { + handleAdvancedConfigUpdate({ fieldMap: JSON.parse(fieldMappingString) }); + setHasUnsavedChanges(false); + setEditFieldMapping(false); + setFieldMapError(undefined); + } else { + setHasUnsavedChanges(true); + setIsFieldMapValid(false); + setFieldMapError(invalidFieldMapMessage); + } + }; + + return ( + + {/* INFERENCE CONFIG */} + + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.inferenceConfigurationTitle', + { defaultMessage: 'Inference configuration' } + )} +

+
+ + +

+ + Learn more. + + ), + }} + /> +

+
+
+ + + + { + if (!editInferenceConfig === false) { + setInferenceConfigError(undefined); + setIsInferenceConfigValid(true); + } + setEditInferenceConfig(!editInferenceConfig); + }} + > + {editInferenceConfig ? CANCEL_EDIT_MESSAGE : EDIT_MESSAGE} + + + + {editInferenceConfig ? ( + + ) : null} + + + {editInferenceConfig ? ( + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.resetInferenceConfigButton', + { defaultMessage: 'Reset' } + )} + + ) : null} + +
+ } + error={inferenceConfigError ?? inferenceConfigError} + isInvalid={inferenceConfigError !== undefined || inferenceConfigError !== undefined} + > + {editInferenceConfig ? ( + + ) : ( + + {JSON.stringify(inferenceConfig, null, 2)} + + )} + +
+
+ + {/* FIELD MAP */} + + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.fieldMapTitle', + { defaultMessage: 'Fields' } + )} +

+
+ + +

+ setIsPopoverOpen(!isPopoverOpen)}> + You can review them here. + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + anchorPosition="downLeft" + > + + {JSON.stringify(modelInputFields, null, 2)} + + + ), + }} + /> +

+

+ {'field_map'}, + inferenceDocsLink: ( + + Learn more. + + ), + }} + /> +

+
+
+ + + + { + const editingState = !editFieldMapping; + if (editingState === false) { + setFieldMapError(undefined); + setIsFieldMapValid(true); + setHasUnsavedChanges(false); + } + setEditFieldMapping(editingState); + }} + > + {editFieldMapping + ? CANCEL_EDIT_MESSAGE + : fieldMap !== undefined + ? EDIT_MESSAGE + : CREATE_FIELD_MAPPING_MESSAGE} + + + {editFieldMapping ? ( + + + + ) : null} + {editFieldMapping ? ( + + + {CLEAR_BUTTON_LABEL} + + + ) : null} +
+ } + error={fieldMapError} + isInvalid={fieldMapError !== undefined} + > + <> + {!editFieldMapping ? ( + + {JSON.stringify(fieldMap ?? {}, null, 2)} + + ) : null} + {editFieldMapping ? ( + <> + + + + ) : null} + + +
+ + + {/* ADDITIONAL ADVANCED SETTINGS */} + + + + + ); + } +); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/review_and_create_pipeline.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/review_and_create_pipeline.tsx new file mode 100644 index 0000000000000..352f11a0ba867 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/review_and_create_pipeline.tsx @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useMemo } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { + EuiAccordion, + EuiCallOut, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSpacer, + EuiTitle, + EuiText, + htmlIdGenerator, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; +import { useMlKibana } from '../../../contexts/kibana'; + +const MANAGEMENT_APP_ID = 'management'; + +interface Props { + inferencePipeline: IngestPipeline; + modelType?: string; + pipelineName: string; + pipelineCreated: boolean; + pipelineError?: string; +} + +export const ReviewAndCreatePipeline: FC = ({ + inferencePipeline, + modelType, + pipelineName, + pipelineCreated, + pipelineError, +}) => { + const { + services: { + application, + docLinks: { links }, + }, + } = useMlKibana(); + + const inferenceProcessorLink = + modelType === 'regression' + ? links.ingest.inferenceRegression + : links.ingest.inferenceClassification; + + const accordionId = useMemo(() => htmlIdGenerator()(), []); + + const configCodeBlock = useMemo( + () => ( + + {JSON.stringify(inferencePipeline ?? {}, null, 2)} + + ), + [inferencePipeline] + ); + + return ( + <> + + + {pipelineCreated === false ? ( + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.review.title', + { + defaultMessage: "Review the pipeline configuration for '{pipelineName}'", + values: { pipelineName }, + } + )} +

+
+ ) : null} + <> + + {pipelineCreated === true && pipelineError === undefined ? ( + +

+ + {'reindexing'} + + ), + }} + /> + {application.capabilities.management?.ingest?.ingest_pipelines ? ( + { + await application.navigateToApp(MANAGEMENT_APP_ID, { + path: `/ingest/ingest_pipelines/?pipeline=${pipelineName}`, + openInNewTab: true, + }); + }} + target="_blank" + external + > + {'Ingest Pipelines'} + + ), + }} + /> + ) : null} +

+
+ ) : null} + {pipelineError !== undefined ? ( + +

{pipelineError}

+

+ + {'ingest pipeline'} + + ), + inferencePipelineConfigLink: ( + + {'inference processor'} + + ), + }} + /> +

+
+ ) : null} + +
+ + +

+ {!pipelineCreated ? ( + + ) : null} +

+
+
+ + {pipelineCreated ? ( + <> + + + } + > + {configCodeBlock} + + + ) : ( + [configCodeBlock] + )} + +
+ + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/save_changes_button.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/save_changes_button.tsx new file mode 100644 index 0000000000000..73e763d153799 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/save_changes_button.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface SaveChangesButtonProps { + onClick: () => void; + disabled: boolean; +} + +export const SaveChangesButton: FC = ({ onClick, disabled }) => ( + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.saveChangesButton', + { defaultMessage: 'Save changes' } + )} + +); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/test_pipeline.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/test_pipeline.tsx new file mode 100644 index 0000000000000..19120de441f4c --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/test_pipeline.tsx @@ -0,0 +1,279 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, memo, useEffect, useCallback, useState } from 'react'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { + EuiButton, + EuiButtonEmpty, + EuiCode, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiResizableContainer, + EuiSpacer, + EuiTitle, + EuiText, + useIsWithinMaxBreakpoint, + EuiPanel, +} from '@elastic/eui'; + +import { IngestSimulateDocument } from '@elastic/elasticsearch/lib/api/types'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { useMlApiContext, useMlKibana } from '../../../contexts/kibana'; +import { getPipelineConfig } from '../get_pipeline_config'; +import { isValidJson } from '../../../../../common/util/validation_utils'; +import type { MlInferenceState } from '../types'; + +interface Props { + sourceIndex?: string; + state: MlInferenceState; +} + +export const TestPipeline: FC = memo(({ state, sourceIndex }) => { + const [simulatePipelineResult, setSimulatePipelineResult] = useState< + undefined | estypes.IngestSimulateResponse + >(); + const [simulatePipelineError, setSimulatePipelineError] = useState(); + const [sampleDocsString, setSampleDocsString] = useState(''); + const [isValid, setIsValid] = useState(true); + const { + esSearch, + trainedModels: { trainedModelPipelineSimulate }, + } = useMlApiContext(); + const { + notifications: { toasts }, + } = useMlKibana(); + + const isSmallerViewport = useIsWithinMaxBreakpoint('s'); + + const simulatePipeline = async () => { + try { + const pipelineConfig = getPipelineConfig(state); + const result = await trainedModelPipelineSimulate( + pipelineConfig, + JSON.parse(sampleDocsString) as IngestSimulateDocument[] + ); + setSimulatePipelineResult(result); + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + const errorProperties = extractErrorProperties(error); + setSimulatePipelineError(error); + toasts.danger({ + title: i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.errorSimulatingPipeline', + { + defaultMessage: 'Unable to simulate pipeline.', + } + ), + body: errorProperties.message, + toastLifeTimeMs: 5000, + }); + } + }; + + const clearResults = () => { + setSimulatePipelineResult(undefined); + setSimulatePipelineError(undefined); + }; + + const onChange = (json: string) => { + setSampleDocsString(json); + const valid = isValidJson(json); + setIsValid(valid); + }; + + const getSampleDocs = useCallback(async () => { + let records: IngestSimulateDocument[] = []; + let resp; + + try { + resp = await esSearch({ + index: sourceIndex, + body: { + size: 1, + }, + }); + + if (resp && resp.hits.total.value > 0) { + records = resp.hits.hits; + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } + setSampleDocsString(JSON.stringify(records, null, 2)); + setIsValid(true); + }, [sourceIndex, esSearch]); + + useEffect( + function fetchSampleDocsFromSource() { + if (sourceIndex) { + getSampleDocs(); + } + }, + [sourceIndex, getSampleDocs] + ); + + return ( + <> + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.title', + { defaultMessage: 'Test the pipeline results' } + )} +

+
+
+ + +

+ + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.optionalCallout', + { defaultMessage: 'This is an optional step.' } + )} + +   + {' '} + {state.targetField && ( + {state.targetField} }} + /> + )} +

+
+
+
+ + + + + + +
+ + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.runButton', + { defaultMessage: 'Simulate pipeline' } + )} + +
+
+ + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.clearResultsButton', + { defaultMessage: 'Clear results' } + )} + + + + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.resetSampleDocsButton', + { defaultMessage: 'Reset sample docs' } + )} + + +
+ +
+ + + + +
+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.subtitle.documents', + { defaultMessage: 'Raw document' } + )} +
+
+
+ + +
+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.test.subtitle.result', + { defaultMessage: 'Result' } + )} +
+
+
+
+
+ + + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + + + + + + + + {simulatePipelineError + ? JSON.stringify(simulatePipelineError, null, 2) + : simulatePipelineResult + ? JSON.stringify(simulatePipelineResult, null, 2) + : '{}'} + + + + )} + + + +
+
+ + ); +}); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/constants.ts b/x-pack/plugins/ml/public/application/components/ml_inference/constants.ts new file mode 100644 index 0000000000000..8bf26c55b1b6d --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/constants.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 { i18n } from '@kbn/i18n'; + +export const ADD_INFERENCE_PIPELINE_STEPS = { + DETAILS: 'Details', + CONFIGURE_PROCESSOR: 'Configure processor', + ON_FAILURE: 'Failure handling', + TEST: 'Test', + CREATE: 'create', +} as const; + +export const CANCEL_BUTTON_LABEL = i18n.translate( + 'xpack.ml.trainedModels.actions.cancelButtonLabel', + { defaultMessage: 'Cancel' } +); + +export const CLEAR_BUTTON_LABEL = i18n.translate( + 'xpack.ml.trainedModels.actions.clearButtonLabel', + { defaultMessage: 'Clear' } +); + +export const CLOSE_BUTTON_LABEL = i18n.translate( + 'xpack.ml.trainedModels.actions.closeButtonLabel', + { defaultMessage: 'Close' } +); + +export const BACK_BUTTON_LABEL = i18n.translate('xpack.ml.trainedModels.actions.backButtonLabel', { + defaultMessage: 'Back', +}); + +export const CONTINUE_BUTTON_LABEL = i18n.translate( + 'xpack.ml.trainedModels.actions.continueButtonLabel', + { defaultMessage: 'Continue' } +); + +export const EDIT_MESSAGE = i18n.translate( + 'xpack.ml.trainedModels.actions.create.advancedDetails.editButtonText', + { + defaultMessage: 'Edit', + } +); + +export const CREATE_FIELD_MAPPING_MESSAGE = i18n.translate( + 'xpack.ml.trainedModels.actions.create.advancedDetails.createFieldMapText', + { + defaultMessage: 'Create field map', + } +); + +export const CANCEL_EDIT_MESSAGE = i18n.translate( + 'xpack.ml.trainedModels.actions.create.advancedDetails.cancelEditButtonText', + { + defaultMessage: 'Cancel', + } +); diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/get_pipeline_config.ts b/x-pack/plugins/ml/public/application/components/ml_inference/get_pipeline_config.ts new file mode 100644 index 0000000000000..d7752a069150e --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/get_pipeline_config.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { MlInferenceState } from './types'; + +export function getPipelineConfig(state: MlInferenceState) { + const { + condition, + fieldMap, + ignoreFailure, + inferenceConfig, + modelId, + onFailure, + pipelineDescription, + tag, + targetField, + } = state; + return { + description: pipelineDescription, + processors: [ + { + inference: { + model_id: modelId, + ignore_failure: ignoreFailure, + ...(targetField && targetField !== '' ? { target_field: targetField } : {}), + ...(fieldMap && Object.keys(fieldMap).length > 0 ? { field_map: fieldMap } : {}), + ...(inferenceConfig && Object.keys(inferenceConfig).length > 0 + ? { inference_config: inferenceConfig } + : {}), + ...(condition && condition !== '' ? { if: condition } : {}), + ...(tag && tag !== '' ? { tag } : {}), + ...(onFailure && Object.keys(onFailure).length > 0 ? { on_failure: onFailure } : {}), + }, + }, + ], + }; +} diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/get_steps.ts b/x-pack/plugins/ml/public/application/components/ml_inference/get_steps.ts new file mode 100644 index 0000000000000..a7d3ea17de099 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/get_steps.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { AddInferencePipelineSteps } from './types'; +import { ADD_INFERENCE_PIPELINE_STEPS } from './constants'; + +export function getSteps( + step: AddInferencePipelineSteps, + isConfigureStepValid: boolean, + isPipelineDataValid: boolean +) { + let nextStep: AddInferencePipelineSteps | undefined; + let previousStep: AddInferencePipelineSteps | undefined; + let isContinueButtonEnabled = false; + + switch (step) { + case ADD_INFERENCE_PIPELINE_STEPS.DETAILS: + nextStep = ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR; + isContinueButtonEnabled = isConfigureStepValid; + break; + case ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR: + nextStep = ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE; + previousStep = ADD_INFERENCE_PIPELINE_STEPS.DETAILS; + isContinueButtonEnabled = isPipelineDataValid; + break; + case ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE: + nextStep = ADD_INFERENCE_PIPELINE_STEPS.TEST; + previousStep = ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR; + isContinueButtonEnabled = isPipelineDataValid; + break; + case ADD_INFERENCE_PIPELINE_STEPS.TEST: + nextStep = ADD_INFERENCE_PIPELINE_STEPS.CREATE; + previousStep = ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE; + isContinueButtonEnabled = true; + break; + case ADD_INFERENCE_PIPELINE_STEPS.CREATE: + previousStep = ADD_INFERENCE_PIPELINE_STEPS.TEST; + isContinueButtonEnabled = true; + break; + } + + return { nextStep, previousStep, isContinueButtonEnabled }; +} diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/hooks/use_fetch_pipelines.ts b/x-pack/plugins/ml/public/application/components/ml_inference/hooks/use_fetch_pipelines.ts new file mode 100644 index 0000000000000..837aef2f92093 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/hooks/use_fetch_pipelines.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { useMlApiContext, useMlKibana } from '../../../contexts/kibana'; + +export const useFetchPipelines = () => { + const [pipelineNames, setPipelineNames] = useState([]); + const { + notifications: { toasts }, + } = useMlKibana(); + + const { + trainedModels: { getAllIngestPipelines }, + } = useMlApiContext(); + + useEffect(() => { + async function fetchPipelines() { + let names: string[] = []; + try { + const results = await getAllIngestPipelines(); + names = Object.keys(results); + setPipelineNames(names); + } catch (e) { + toasts.danger({ + title: i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.fetchIngestPipelinesError', + { + defaultMessage: 'Unable to fetch ingest pipelines.', + } + ), + body: e.message, + toastLifeTimeMs: 5000, + }); + } + } + + fetchPipelines(); + }, [getAllIngestPipelines, toasts]); + + return pipelineNames; +}; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/index.ts b/x-pack/plugins/ml/public/application/components/ml_inference/index.ts new file mode 100644 index 0000000000000..0c079b0273e98 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/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 { AddInferencePipelineFlyout } from './add_inference_pipeline_flyout'; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/state.ts b/x-pack/plugins/ml/public/application/components/ml_inference/state.ts new file mode 100644 index 0000000000000..6c74e279fa147 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/state.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getAnalysisType } from '@kbn/ml-data-frame-analytics-utils'; +import type { MlInferenceState } from './types'; +import { ModelItem } from '../../model_management/models_list'; + +export const getModelType = (model: ModelItem): string | undefined => { + const analysisConfig = model.metadata?.analytics_config?.analysis; + return analysisConfig !== undefined ? getAnalysisType(analysisConfig) : undefined; +}; + +export const getDefaultOnFailureConfiguration = (): MlInferenceState['onFailure'] => [ + { + set: { + description: "Index document to 'failed-'", + field: '_index', + value: 'failed-{{{ _index }}}', + }, + }, + { + set: { + field: 'event.timestamp', + value: '{{{ _ingest.timestamp }}}', + }, + }, + { + set: { + field: 'event.failure.message', + value: '{{{ _ingest.on_failure_message }}}', + }, + }, + { + set: { + field: 'event.failure.processor_type', + value: '{{{ _ingest.on_failure_processor_type }}}', + }, + }, + { + set: { + field: 'event.failure.processor_tag', + value: '{{{ _ingest.on_failure_processor_tag }}}', + }, + }, + { + set: { + field: 'event.failure.pipeline', + value: '{{{ _ingest.on_failure_pipeline }}}', + }, + }, +]; + +export const getInitialState = (model: ModelItem): MlInferenceState => { + const modelType = getModelType(model); + let targetField; + + if (modelType !== undefined) { + targetField = model.inference_config + ? `ml.inference.${model.inference_config[modelType].results_field}` + : undefined; + } + + return { + condition: undefined, + creatingPipeline: false, + error: false, + fieldMap: undefined, + ignoreFailure: false, + inferenceConfig: model.inference_config, + modelId: model.model_id, + onFailure: getDefaultOnFailureConfiguration(), + pipelineDescription: `Uses the pre-trained data frame analytics model ${model.model_id} to infer against the data that is being ingested in the pipeline`, + pipelineName: `ml-inference-${model.model_id}`, + pipelineCreated: false, + tag: undefined, + takeActionOnFailure: true, + targetField: targetField ?? '', + }; +}; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/types.ts b/x-pack/plugins/ml/public/application/components/ml_inference/types.ts new file mode 100644 index 0000000000000..da0aef1a42154 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/types.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 { IngestInferenceProcessor } from '@elastic/elasticsearch/lib/api/types'; +import { ADD_INFERENCE_PIPELINE_STEPS } from './constants'; + +export type AddInferencePipelineSteps = + typeof ADD_INFERENCE_PIPELINE_STEPS[keyof typeof ADD_INFERENCE_PIPELINE_STEPS]; + +export interface MlInferenceState { + condition?: string; + creatingPipeline: boolean; + error: boolean; + fieldMap?: IngestInferenceProcessor['field_map']; + fieldMapError?: string; + ignoreFailure: boolean; + inferenceConfig: IngestInferenceProcessor['inference_config']; + inferenceConfigError?: string; + modelId: string; + onFailure?: IngestInferenceProcessor['on_failure']; + pipelineName: string; + pipelineNameError?: string; + pipelineDescription: string; + pipelineCreated: boolean; + pipelineError?: string; + tag?: string; + targetField: string; + targetFieldError?: string; + takeActionOnFailure: boolean; +} + +export interface AddInferencePipelineFormErrors { + targetField?: string; + fieldMap?: string; + inferenceConfig?: string; + pipelineName?: string; +} + +export type InferenceModelTypes = 'regression' | 'classification'; + +export interface AdditionalSettings { + condition?: string; + tag?: string; +} diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/validation.ts b/x-pack/plugins/ml/public/application/components/ml_inference/validation.ts new file mode 100644 index 0000000000000..c86389607d54a --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/ml_inference/validation.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { IngestInferenceProcessor } from '@elastic/elasticsearch/lib/api/types'; +import { InferenceModelTypes } from './types'; +import type { AddInferencePipelineFormErrors } from './types'; + +const INVALID_PIPELINE_NAME_ERROR = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.invalidPipelineName', + { + defaultMessage: 'Name must only contain letters, numbers, underscores, and hyphens.', + } +); +const FIELD_REQUIRED_ERROR = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.emptyValueError', + { + defaultMessage: 'Field is required.', + } +); +const NO_EMPTY_INFERENCE_CONFIG_OBJECT = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.noEmptyInferenceConfigObjectError', + { + defaultMessage: 'Inference configuration cannot be an empty object.', + } +); +const PIPELINE_NAME_EXISTS_ERROR = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.pipelineNameExistsError', + { + defaultMessage: 'Name already used by another pipeline.', + } +); +const FIELD_MAP_REQUIRED_FIELDS_ERROR = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.emptyValueError', + { + defaultMessage: 'Field map must include fields expected by the model.', + } +); +const INFERENCE_CONFIG_MODEL_TYPE_ERROR = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.incorrectModelTypeError', + { + defaultMessage: 'Inference configuration inference type must match model type.', + } +); + +const VALID_PIPELINE_NAME_REGEX = /^[\w\-]+$/; +export const isValidPipelineName = (input: string): boolean => { + return input.length > 0 && VALID_PIPELINE_NAME_REGEX.test(input); +}; + +export const validateInferencePipelineConfigurationStep = ( + pipelineName: string, + pipelineNames: string[] +) => { + const errors: AddInferencePipelineFormErrors = {}; + + if (pipelineName.trim().length === 0 || pipelineName === '') { + errors.pipelineName = FIELD_REQUIRED_ERROR; + } else if (!isValidPipelineName(pipelineName)) { + errors.pipelineName = INVALID_PIPELINE_NAME_ERROR; + } + + const pipelineNameExists = pipelineNames.find((name) => name === pipelineName) !== undefined; + + if (pipelineNameExists) { + errors.pipelineName = PIPELINE_NAME_EXISTS_ERROR; + } + + return errors; +}; + +export const validateInferenceConfig = ( + inferenceConfig: IngestInferenceProcessor['inference_config'], + modelType?: InferenceModelTypes +) => { + const inferenceConfigKeys = Object.keys(inferenceConfig ?? {}); + let error; + + // If inference config has been changed, it cannot be an empty object + if (inferenceConfig && Object.keys(inferenceConfig).length === 0) { + error = NO_EMPTY_INFERENCE_CONFIG_OBJECT; + return error; + } + + // If populated, inference config must have the correct model type + if (inferenceConfig && inferenceConfigKeys.length > 0) { + if (modelType === inferenceConfigKeys[0]) { + return error; + } else { + error = INFERENCE_CONFIG_MODEL_TYPE_ERROR; + } + return error; + } + return error; +}; + +export const validateFieldMap = ( + modelInputFields: string[], + fieldMap: IngestInferenceProcessor['field_map'] +) => { + let error; + const fieldMapValues: string[] = Object.values(fieldMap?.field_map ?? {}); + + // If populated, field map must include at least some model input fields as values. + if (fieldMap && fieldMapValues.length > 0) { + if (fieldMapValues.some((v) => modelInputFields.includes(v))) { + return error; + } else { + error = FIELD_MAP_REQUIRED_FIELDS_ERROR; + } + } + + return error; +}; diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts b/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts index 8ed1f396c51d3..852a8b8699512 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts @@ -26,7 +26,9 @@ import type { CasesUiStart } from '@kbn/cases-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { MlServicesContext } from '../../app'; interface StartPlugins { @@ -51,6 +53,8 @@ interface StartPlugins { lens: LensPublicStart; savedObjectsManagement: SavedObjectsManagementPluginStart; savedSearch: SavedSearchPublicPluginStart; + contentManagement: ContentManagementPublicStart; + presentationUtil: PresentationUtilPluginStart; } export type StartServices = CoreStart & StartPlugins & { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.test.tsx index d3e4734f633b3..c17490f4506d9 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.test.tsx @@ -79,6 +79,7 @@ jest.mock('../../../../../contexts/kibana', () => ({ savedObjectsManagement: {}, data: { dataViews: jest.fn() }, savedSearch: jest.fn(), + contentManagement: {}, }, }), useNavigateToPath: () => mockNavigateToPath, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx index 7ca404b036c29..d4987b17aeb59 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx @@ -31,11 +31,10 @@ const fixedPageSize: number = 20; export const SourceSelection: FC = () => { const { services: { - http, - uiSettings, - savedObjectsManagement, savedSearch: savedSearchService, data: { dataViews: dataViewsService }, + contentManagement, + uiSettings, }, } = useMlKibana(); const navigateToPath = useNavigateToPath(); @@ -159,14 +158,12 @@ export const SourceSelection: FC = () => { defaultMessage: 'Data view', } ), - defaultSearchField: 'name', }, ]} fixedPageSize={fixedPageSize} services={{ + contentClient: contentManagement.client, uiSettings, - http, - savedObjectsManagement, }} /> diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx index 9e68d9f6f8c37..1e30004453616 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx @@ -55,10 +55,9 @@ interface Props { export const ChangeDataViewModal: FC = ({ onClose }) => { const { services: { - http, - uiSettings, data: { dataViews }, - savedObjectsManagement, + contentManagement, + uiSettings, }, } = useMlKibana(); const navigateToPath = useNavigateToPath(); @@ -168,15 +167,10 @@ export const ChangeDataViewModal: FC = ({ onClose }) => { defaultMessage: 'Data view', } ), - defaultSearchField: 'name', }, ]} fixedPageSize={fixedPageSize} - services={{ - uiSettings, - http, - savedObjectsManagement, - }} + services={{ contentClient: contentManagement.client, uiSettings }} /> )} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/agg_select/agg_select.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/agg_select/agg_select.tsx index 4c8d2996a318b..824fb52398fd4 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/agg_select/agg_select.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/agg_select/agg_select.tsx @@ -8,6 +8,7 @@ import React, { FC, useContext, useState, useEffect, useMemo } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; import type { Field, Aggregation, AggFieldPair } from '@kbn/ml-anomaly-utils'; +import { EVENT_RATE_FIELD_ID } from '@kbn/ml-anomaly-utils'; import { FieldStatsInfoButton } from '../../../../../../../components/field_stats_flyout/field_stats_info_button'; import { JobCreatorContext } from '../../../job_creator_context'; import { useFieldStatsTrigger } from '../../../../../../../components/field_stats_flyout/use_field_stats_trigger'; @@ -43,7 +44,7 @@ export const AggSelect: FC = ({ fields, changeHandler, selectedOptions, r // create list of labels based on already selected detectors // so they can be removed from the dropdown list const removeLabels = removeOptions.map(createLabel); - const { handleFieldStatsButtonClick } = useFieldStatsTrigger(); + const { handleFieldStatsButtonClick, populatedFields } = useFieldStatsTrigger(); const options: EuiComboBoxOptionOption[] = useMemo( () => @@ -55,6 +56,8 @@ export const AggSelect: FC = ({ fields, changeHandler, selectedOptions, r // for more robust rendering label: ( = ({ fields, changeHandler, selectedOptions, r } return aggOption; }), - [handleFieldStatsButtonClick, fields, removeLabels] + // eslint-disable-next-line react-hooks/exhaustive-deps + [handleFieldStatsButtonClick, fields, removeLabels, populatedFields?.size] ); useEffect(() => { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx index 9d2cfa605f50b..d29654f8e9f8d 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx @@ -19,7 +19,7 @@ export interface PageProps { export const Page: FC = ({ nextStepPath }) => { const RESULTS_PER_PAGE = 20; - const { uiSettings, http, savedObjectsManagement } = useMlKibana().services; + const { contentManagement, uiSettings } = useMlKibana().services; const navigateToPath = useNavigateToPath(); const onObjectSelection = (id: string, type: string) => { @@ -67,14 +67,12 @@ export const Page: FC = ({ nextStepPath }) => { defaultMessage: 'Data view', } ), - defaultSearchField: 'name', }, ]} fixedPageSize={RESULTS_PER_PAGE} services={{ + contentClient: contentManagement.client, uiSettings, - http, - savedObjectsManagement, }} /> diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index e4b79eafb896d..6941e53cd68b2 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -70,6 +70,11 @@ export const JobsListPage: FC<{ const I18nContext = coreStart.i18n.Context; const theme$ = coreStart.theme.theme$; + const mlServices = useMemo( + () => getMlGlobalServices(coreStart.http, usageCollection), + [coreStart.http, usageCollection] + ); + const check = async () => { try { await checkGetManagementMlJobsResolver(mlApiServices); @@ -122,7 +127,7 @@ export const JobsListPage: FC<{ usageCollection, fieldFormats, spacesApi, - mlServices: getMlGlobalServices(coreStart.http, usageCollection), + mlServices, }} > diff --git a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx index 5cdc922824ae7..02738f3bebe51 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx @@ -122,13 +122,27 @@ export const AllocatedModels: FC = ({ }, }, { - field: 'node.throughput_last_minute', - name: i18n.translate( - 'xpack.ml.trainedModels.nodesList.modelsList.throughputLastMinuteHeader', - { - defaultMessage: 'Throughput', - } + name: ( + + + {i18n.translate( + 'xpack.ml.trainedModels.nodesList.modelsList.throughputLastMinuteHeader', + { + defaultMessage: 'Throughput', + } + )} + + + ), + field: 'node.throughput_last_minute', width: '100px', truncateText: false, 'data-test-subj': 'mlAllocatedModelsTableThroughput', diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index 09ba11f56c747..9cd0794d1b488 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -35,6 +35,7 @@ import { ModelItem } from './models_list'; export function useModelActions({ onTestAction, onModelsDeleteRequest, + onModelDeployRequest, onLoading, isLoading, fetchModels, @@ -43,6 +44,7 @@ export function useModelActions({ isLoading: boolean; onTestAction: (model: ModelItem) => void; onModelsDeleteRequest: (models: ModelItem[]) => void; + onModelDeployRequest: (model: ModelItem) => void; onLoading: (isLoading: boolean) => void; fetchModels: () => Promise; modelAndDeploymentIds: string[]; @@ -412,6 +414,54 @@ export function useModelActions({ } }, }, + { + name: (model) => { + const hasDeployments = model.state === MODEL_STATE.STARTED; + return ( + + <> + {i18n.translate('xpack.ml.trainedModels.modelsList.deployModelActionLabel', { + defaultMessage: 'Deploy model', + })} + + + ); + }, + description: i18n.translate('xpack.ml.trainedModels.modelsList.deployModelActionLabel', { + defaultMessage: 'Deploy model', + }), + 'data-test-subj': 'mlModelsTableRowDeployAction', + icon: 'continuityAbove', + type: 'icon', + isPrimary: false, + onClick: (model) => { + onModelDeployRequest(model); + }, + available: (item) => { + const isDfaTrainedModel = item.metadata?.analytics_config !== undefined; + return ( + isDfaTrainedModel && + !isBuiltInModel(item) && + !item.putModelConfig && + canManageIngestPipelines + ); + }, + enabled: (item) => { + return item.state !== MODEL_STATE.STARTED; + }, + }, { name: (model) => { const hasDeployments = model.state === MODEL_STATE.STARTED; @@ -492,6 +542,7 @@ export function useModelActions({ displayErrorToast, getUserConfirmation, onModelsDeleteRequest, + onModelDeployRequest, canDeleteTrainedModels, isBuiltInModel, onTestAction, diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index d5f15c10aa2c6..653963155bc68 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -62,6 +62,7 @@ import { useFieldFormatter } from '../contexts/kibana/use_field_formatter'; import { useRefresh } from '../routing/use_refresh'; import { SavedObjectsWarning } from '../components/saved_objects_warning'; import { TestTrainedModelFlyout } from './test_models'; +import { AddInferencePipelineFlyout } from '../components/ml_inference'; type Stats = Omit; @@ -134,6 +135,7 @@ export const ModelsList: FC = ({ const [items, setItems] = useState([]); const [selectedModels, setSelectedModels] = useState([]); const [modelsToDelete, setModelsToDelete] = useState([]); + const [modelToDeploy, setModelToDeploy] = useState(); const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( {} ); @@ -349,6 +351,7 @@ export const ModelsList: FC = ({ fetchModels: fetchModelsData, onTestAction: setModelToTest, onModelsDeleteRequest: setModelsToDelete, + onModelDeployRequest: setModelToDeploy, onLoading: setIsLoading, modelAndDeploymentIds, }); @@ -642,6 +645,12 @@ export const ModelsList: FC = ({ {modelToTest === null ? null : ( )} + {modelToDeploy !== undefined ? ( + + ) : null} ); }; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts index 0ea4b1d1fde4b..e6b9c1a5badc3 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts @@ -6,6 +6,7 @@ */ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; import { useMemo } from 'react'; import type { HttpFetchQuery } from '@kbn/core/public'; @@ -58,7 +59,6 @@ export function trainedModelsApiProvider(httpService: HttpService) { return { /** * Fetches configuration information for a trained inference model. - * * @param modelId - Model ID, collection of Model IDs or Model ID pattern. * Fetches all In case nothing is provided. * @param params - Optional query params @@ -76,7 +76,6 @@ export function trainedModelsApiProvider(httpService: HttpService) { /** * Fetches usage information for trained inference models. - * * @param modelId - Model ID, collection of Model IDs or Model ID pattern. * Fetches all In case nothing is provided. * @param params - Optional query params @@ -93,7 +92,6 @@ export function trainedModelsApiProvider(httpService: HttpService) { /** * Fetches pipelines associated with provided models - * * @param modelId - Model ID, collection of Model IDs. */ getTrainedModelPipelines(modelId: string | string[]) { @@ -109,9 +107,31 @@ export function trainedModelsApiProvider(httpService: HttpService) { }); }, + /** + * Fetches all ingest pipelines + */ + getAllIngestPipelines() { + return httpService.http({ + path: `${ML_INTERNAL_BASE_PATH}/trained_models/ingest_pipelines`, + method: 'GET', + version: '1', + }); + }, + + /** + * Creates inference pipeline + */ + createInferencePipeline(pipelineName: string, pipeline: IngestPipeline) { + return httpService.http({ + path: `${ML_INTERNAL_BASE_PATH}/trained_models/create_inference_pipeline`, + method: 'POST', + body: JSON.stringify({ pipeline, pipelineName }), + version: '1', + }); + }, + /** * Deletes an existing trained inference model. - * * @param modelId - Model ID */ deleteTrainedModel( diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts index 806856fbf06ad..3e7f958ea778e 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from '@kbn/core/public'; import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; +import type { IAnomalySwimlaneEmbeddable } from './anomaly_swimlane_embeddable'; import { PLUGIN_ID, PLUGIN_ICON, ML_APP_NAME } from '../../../common/constants/app'; import { HttpService } from '../../application/services/http_service'; import type { MlPluginStart, MlStartDependencies } from '../../plugin'; @@ -94,7 +95,7 @@ export class AnomalySwimlaneEmbeddableFactory public async create( initialInput: AnomalySwimlaneEmbeddableInput, parent?: IContainer - ): Promise { + ): Promise> { const services = await this.getServices(); const { AnomalySwimlaneEmbeddable } = await import('./anomaly_swimlane_embeddable'); return new AnomalySwimlaneEmbeddable(initialInput, services, parent); diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index f9f3b6d22b0fc..c66d4c6ec8d33 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -32,6 +32,7 @@ import type { LicenseManagementUIPluginSetup } from '@kbn/license-management-plu import type { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { SecurityPluginStart } from '@kbn/security-plugin/public'; import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import type { MapsStartApi, MapsSetupApi } from '@kbn/maps-plugin/public'; import { @@ -46,6 +47,7 @@ import type { DashboardSetup, DashboardStart } from '@kbn/dashboard-plugin/publi import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { CasesUiSetup, CasesUiStart } from '@kbn/cases-plugin/public'; import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { registerManagementSection } from './application/management'; import { MlLocatorDefinition, type MlLocator } from './locator'; import { setDependencyCache } from './application/util/dependency_cache'; @@ -73,6 +75,8 @@ export interface MlStartDependencies { security: SecurityPluginStart; savedObjectsManagement: SavedObjectsManagementPluginStart; savedSearch: SavedSearchPublicPluginStart; + contentManagement: ContentManagementPublicStart; + presentationUtil: PresentationUtilPluginStart; } export interface MlSetupDependencies { @@ -142,6 +146,8 @@ export class MlPlugin implements Plugin { cases: pluginsStart.cases, savedObjectsManagement: pluginsStart.savedObjectsManagement, savedSearch: pluginsStart.savedSearch, + contentManagement: pluginsStart.contentManagement, + presentationUtil: pluginsStart.presentationUtil, }, params ); diff --git a/x-pack/plugins/ml/scripts/apidoc_scripts/apidoc_config/apidoc.json b/x-pack/plugins/ml/scripts/apidoc_scripts/apidoc_config/apidoc.json index 978dbf9dc94f3..90dbdbd9c121f 100644 --- a/x-pack/plugins/ml/scripts/apidoc_scripts/apidoc_config/apidoc.json +++ b/x-pack/plugins/ml/scripts/apidoc_scripts/apidoc_config/apidoc.json @@ -177,6 +177,8 @@ "DeleteTrainedModel", "SimulateIngestPipeline", "InferTrainedModelDeployment", + "CreateInferencePipeline", + "GetIngestPipelines", "Alerting", "PreviewAlert", diff --git a/x-pack/plugins/ml/server/models/model_management/models_provider.ts b/x-pack/plugins/ml/server/models/model_management/models_provider.ts index 702e8454660f6..e7cfcbe7fd50d 100644 --- a/x-pack/plugins/ml/server/models/model_management/models_provider.ts +++ b/x-pack/plugins/ml/server/models/model_management/models_provider.ts @@ -6,6 +6,11 @@ */ import type { IScopedClusterClient } from '@kbn/core/server'; +import { + IngestPipeline, + IngestSimulateDocument, + IngestSimulateRequest, +} from '@elastic/elasticsearch/lib/api/types'; import type { PipelineDefinition } from '../../../common/types/trained_models'; export type ModelService = ReturnType; @@ -64,5 +69,64 @@ export function modelsProvider(client: IScopedClusterClient) { pipelinesIds.map((id) => client.asCurrentUser.ingest.deletePipeline({ id })) ); }, + + /** + * Simulates the effect of the pipeline on given document. + * + */ + async simulatePipeline(docs: IngestSimulateDocument[], pipelineConfig: IngestPipeline) { + const simulateRequest: IngestSimulateRequest = { + docs, + pipeline: pipelineConfig, + }; + let result = {}; + try { + result = await client.asCurrentUser.ingest.simulate(simulateRequest); + } catch (error) { + if (error.statusCode === 404) { + // ES returns 404 when there are no pipelines + // Instead, we should return an empty response and a 200 + return result; + } + throw error; + } + + return result; + }, + + /** + * Creates the pipeline + * + */ + async createInferencePipeline(pipelineConfig: IngestPipeline, pipelineName: string) { + let result = {}; + + result = await client.asCurrentUser.ingest.putPipeline({ + id: pipelineName, + ...pipelineConfig, + }); + + return result; + }, + + /** + * Retrieves existing pipelines. + * + */ + async getPipelines() { + let result = {}; + try { + result = await client.asCurrentUser.ingest.getPipeline(); + } catch (error) { + if (error.statusCode === 404) { + // ES returns 404 when there are no pipelines + // Instead, we should return an empty response and a 200 + return result; + } + throw error; + } + + return result; + }, }; } diff --git a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts index e15fd108c5f5b..21b62c6f5ce42 100644 --- a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts @@ -77,3 +77,13 @@ export const deleteTrainedModelQuerySchema = schema.object({ with_pipelines: schema.maybe(schema.boolean({ defaultValue: false })), force: schema.maybe(schema.boolean({ defaultValue: false })), }); + +export const createIngestPipelineSchema = schema.object({ + pipelineName: schema.string(), + pipeline: schema.maybe( + schema.object({ + processors: schema.arrayOf(schema.any()), + description: schema.maybe(schema.string()), + }) + ), +}); diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts index cefb8a9dce0fd..c85429f41a72a 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -22,6 +22,7 @@ import { putTrainedModelQuerySchema, threadingParamsSchema, updateDeploymentParamsSchema, + createIngestPipelineSchema, } from './schemas/inference_schema'; import { TrainedModelConfigResponse } from '../../common/types/trained_models'; import { mlLog } from '../lib/log'; @@ -237,6 +238,78 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) }) ); + /** + * @apiGroup TrainedModels + * + * @api {get} /internal/ml/trained_models/ingest_pipelines Get ingest pipelines + * @apiName GetIngestPipelines + * @apiDescription Retrieves pipelines + */ + router.versioned + .get({ + path: `${ML_INTERNAL_BASE_PATH}/trained_models/ingest_pipelines`, + access: 'internal', + options: { + tags: ['access:ml:canGetTrainedModels'], // TODO: update permissions + }, + }) + .addVersion( + { + version: '1', + validate: false, + }, + routeGuard.fullLicenseAPIGuard(async ({ client, request, mlClient, response }) => { + try { + const body = await modelsProvider(client).getPipelines(); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup TrainedModels + * + * @api {post} /internal/ml/trained_models/create_inference_pipeline creates the pipeline with inference processor + * @apiName CreateInferencePipeline + * @apiDescription Creates the inference pipeline + */ + router.versioned + .post({ + path: `${ML_INTERNAL_BASE_PATH}/trained_models/create_inference_pipeline`, + access: 'internal', + options: { + tags: ['access:ml:canCreateTrainedModels'], + }, + }) + .addVersion( + { + version: '1', + validate: { + request: { + body: createIngestPipelineSchema, + }, + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ client, request, mlClient, response }) => { + try { + const { pipeline, pipelineName } = request.body; + const body = await modelsProvider(client).createInferencePipeline( + pipeline!, + pipelineName + ); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + /** * @apiGroup TrainedModels * diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 13560d5e0b962..ff3b8076a8ba1 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -100,6 +100,8 @@ "@kbn/core-notifications-browser-mocks", "@kbn/unified-field-list", "@kbn/core-ui-settings-browser", + "@kbn/content-management-plugin", "@kbn/ml-in-memory-table", + "@kbn/presentation-util-plugin", ], } diff --git a/x-pack/plugins/observability/docs/openapi/slo/bundled.json b/x-pack/plugins/observability/docs/openapi/slo/bundled.json index efc5fd9c5b15f..a949bc868850e 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/bundled.json +++ b/x-pack/plugins/observability/docs/openapi/slo/bundled.json @@ -1264,6 +1264,12 @@ "error_budget": { "title": "Error budget", "type": "object", + "required": [ + "initial", + "consumed", + "remaining", + "isEstimated" + ], "properties": { "initial": { "type": "number", @@ -1291,6 +1297,11 @@ "title": "Summary", "type": "object", "description": "The SLO computed data", + "required": [ + "status", + "sliValue", + "errorBudget" + ], "properties": { "status": { "$ref": "#/components/schemas/summary_status" @@ -1307,6 +1318,23 @@ "slo_response": { "title": "SLO response", "type": "object", + "required": [ + "id", + "name", + "description", + "indicator", + "timeWindow", + "budgetingMethod", + "objective", + "settings", + "revision", + "summary", + "enabled", + "groupBy", + "instanceId", + "createdAt", + "updatedAt" + ], "properties": { "id": { "description": "The identifier of the SLO.", diff --git a/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml index ec2aa41bc77af..1a134dd4d5d6b 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml @@ -867,6 +867,11 @@ components: error_budget: title: Error budget type: object + required: + - initial + - consumed + - remaining + - isEstimated properties: initial: type: number @@ -888,6 +893,10 @@ components: title: Summary type: object description: The SLO computed data + required: + - status + - sliValue + - errorBudget properties: status: $ref: '#/components/schemas/summary_status' @@ -899,6 +908,22 @@ components: slo_response: title: SLO response type: object + required: + - id + - name + - description + - indicator + - timeWindow + - budgetingMethod + - objective + - settings + - revision + - summary + - enabled + - groupBy + - instanceId + - createdAt + - updatedAt properties: id: description: The identifier of the SLO. diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/error_budget.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/error_budget.yaml index c344c8472821d..3fc9505989fe9 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/error_budget.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/error_budget.yaml @@ -1,5 +1,10 @@ title: Error budget type: object +required: + - initial + - consumed + - remaining + - isEstimated properties: initial: type: number diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml index 0663c31e40a0f..b4e5eb85f3264 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml @@ -1,5 +1,21 @@ title: SLO response type: object +required: + - id + - name + - description + - indicator + - timeWindow + - budgetingMethod + - objective + - settings + - revision + - summary + - enabled + - groupBy + - instanceId + - createdAt + - updatedAt properties: id: description: The identifier of the SLO. diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/summary.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/summary.yaml index a7b1be7d053b1..5dfb724f31834 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/summary.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/summary.yaml @@ -1,6 +1,10 @@ title: Summary type: object description: The SLO computed data +required: + - status + - sliValue + - errorBudget properties: status: $ref: './summary_status.yaml' diff --git a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.stories.tsx b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.stories.tsx index 08fa152301d43..3aed4658ab766 100644 --- a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.stories.tsx +++ b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.stories.tsx @@ -25,4 +25,4 @@ const Template: ComponentStory = (props: Props) => ( ); export const Default = Template.bind({}); -Default.args = { activeAlerts: { count: 2, ruleIds: ['rule-1', 'rule-2'] } }; +Default.args = { activeAlerts: { count: 2 } }; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts index 319f3e57a4a4c..580d72e550e6b 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts @@ -19,7 +19,6 @@ interface Params { export interface ActiveAlerts { count: number; - ruleIds: string[]; } type ActiveAlertsMap = Record; @@ -37,7 +36,6 @@ interface FindApiResponse { buckets: Array<{ key: string; doc_count: number; - perRuleId: { buckets: Array<{ key: string; doc_count: number }> }; }>; }; }; @@ -77,20 +75,22 @@ export function useFetchActiveAlerts({ sloIds = [] }: Params): UseFetchActiveAle }, }, ], + should: [ + { + terms: { + 'kibana.alert.rule.parameters.sloId': sloIds, + }, + }, + ], + minimum_should_match: 1, }, }, aggs: { perSloId: { terms: { + size: sloIds.length, field: 'kibana.alert.rule.parameters.sloId', }, - aggs: { - perRuleId: { - terms: { - field: 'kibana.alert.rule.uuid', - }, - }, - }, }, }, }), @@ -102,7 +102,6 @@ export function useFetchActiveAlerts({ sloIds = [] }: Params): UseFetchActiveAle ...acc, [bucket.key]: { count: bucket.doc_count ?? 0, - ruleIds: bucket.perRuleId.buckets.map((rule) => rule.key), } as ActiveAlerts, }), {} diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts index bd83b069133c3..2f51e4d7faf26 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts @@ -18,6 +18,8 @@ export interface UseFetchIndexPatternFieldsResponse { export interface Field { name: string; type: string; + aggregatable: boolean; + searchable: boolean; } export function useFetchIndexPatternFields( diff --git a/x-pack/plugins/observability/public/hooks/use_fetch_rule.ts b/x-pack/plugins/observability/public/hooks/use_fetch_rule.ts index 83dc3e9737de2..44f1d6f5a37b7 100644 --- a/x-pack/plugins/observability/public/hooks/use_fetch_rule.ts +++ b/x-pack/plugins/observability/public/hooks/use_fetch_rule.ts @@ -61,12 +61,10 @@ export function useFetchRule({ ruleId }: { ruleId?: string }): UseFetchRuleRespo onError: (error: Error) => { toasts.addError(error, { title: i18n.translate('xpack.observability.ruleDetails.ruleLoadError', { - defaultMessage: 'Unable to load rule. Reason: {message}', - values: { - message: - error instanceof Error ? error.message : typeof error === 'string' ? error : '', - }, + defaultMessage: 'Unable to load rule', }), + toastMessage: + error instanceof Error ? error.message : typeof error === 'string' ? error : '', }); }, } diff --git a/x-pack/plugins/observability/public/hooks/use_fetch_rule_types.ts b/x-pack/plugins/observability/public/hooks/use_fetch_rule_types.ts index 079aa32300cc6..de5bb69e28172 100644 --- a/x-pack/plugins/observability/public/hooks/use_fetch_rule_types.ts +++ b/x-pack/plugins/observability/public/hooks/use_fetch_rule_types.ts @@ -65,13 +65,11 @@ export function useFetchRuleTypes({ refetchOnWindowFocus: false, onError: (error: Error) => { toasts.addError(error, { - title: i18n.translate('xpack.observability.ruleDetails.ruleLoadError', { - defaultMessage: 'Unable to load rule. Reason: {message}', - values: { - message: - error instanceof Error ? error.message : typeof error === 'string' ? error : '', - }, + title: i18n.translate('xpack.observability.ruleDetails.ruleTypeLoadError', { + defaultMessage: 'Unable to load rule type.', }), + toastMessage: + error instanceof Error ? error.message : typeof error === 'string' ? error : '', }); }, } diff --git a/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx b/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx index d2c47da425a01..ea80fc8f8cc1c 100644 --- a/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx +++ b/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx @@ -18,6 +18,7 @@ export function useGetUserCasesPermissions() { update: false, delete: false, push: false, + connectors: false, }); const uiCapabilities = useKibana().services.application.capabilities; @@ -33,6 +34,7 @@ export function useGetUserCasesPermissions() { update: casesCapabilities.update, delete: casesCapabilities.delete, push: casesCapabilities.push, + connectors: casesCapabilities.connectors, }); }, [ casesCapabilities.all, @@ -41,6 +43,7 @@ export function useGetUserCasesPermissions() { casesCapabilities.update, casesCapabilities.delete, casesCapabilities.push, + casesCapabilities.connectors, ]); return casesPermissions; diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx index 8ef8f9e74e130..cd4f69879077b 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx @@ -14,13 +14,16 @@ import { render } from '../../../utils/test_helper'; import { useKibana } from '../../../utils/kibana_react'; import { kibanaStartMock } from '../../../utils/kibana_react.mock'; import { alertWithTags, mockAlertUuid } from '../mock/alert'; +import { useFetchRule } from '../../../hooks/use_fetch_rule'; import { HeaderActions } from './header_actions'; import { CasesUiStart } from '@kbn/cases-plugin/public'; jest.mock('../../../utils/kibana_react'); +jest.mock('../../../hooks/use_fetch_rule'); const useKibanaMock = useKibana as jest.Mock; +const useFetchRuleMock = useFetchRule as jest.Mock; const mockCases = casesPluginMock.createStartContract(); const mockKibana = () => { @@ -33,61 +36,82 @@ const mockKibana = () => { }); }; -const ruleId = '123'; -const ruleName = '456'; - -jest.mock('../../../hooks/use_fetch_rule', () => { - return { - useFetchRule: () => ({ - reloadRule: jest.fn(), - rule: { - id: ruleId, - name: ruleName, - }, - }), - }; -}); +const mockRuleId = '123'; +const mockRuleName = '456'; + +const mockUseFetchRuleWithData = () => { + useFetchRuleMock.mockReturnValue({ + reloadRule: jest.fn(), + rule: { + id: mockRuleId, + name: mockRuleName, + }, + }); +}; +const mockUseFetchRuleWithoutData = () => { + useFetchRuleMock.mockReturnValue({ + reloadRule: jest.fn(), + rule: null, + }); +}; describe('Header Actions', () => { - beforeEach(() => { + afterAll(() => { jest.clearAllMocks(); - mockKibana(); }); - it('should display an actions button', () => { - const { queryByTestId } = render(); - expect(queryByTestId('alert-details-header-actions-menu-button')).toBeTruthy(); - }); + describe('Header Actions - Enabled', () => { + beforeEach(() => { + mockKibana(); + mockUseFetchRuleWithData(); + }); - describe('when clicking the actions button', () => { - it('should offer an "add to case" button which opens the add to case modal', async () => { - let attachments: any[] = []; + it('should display an actions button', () => { + const { queryByTestId } = render(); + expect(queryByTestId('alert-details-header-actions-menu-button')).toBeTruthy(); + }); + + describe('when clicking the actions button', () => { + it('should offer an "add to case" button which opens the add to case modal', async () => { + let attachments: any[] = []; - const useCasesAddToExistingCaseModalMock: any = jest.fn().mockImplementation(() => ({ - open: ({ getAttachments }: { getAttachments: () => any[] }) => { - attachments = getAttachments(); - }, - })) as CasesUiStart['hooks']['useCasesAddToExistingCaseModal']; + const useCasesAddToExistingCaseModalMock: any = jest.fn().mockImplementation(() => ({ + open: ({ getAttachments }: { getAttachments: () => any[] }) => { + attachments = getAttachments(); + }, + })) as CasesUiStart['hooks']['useCasesAddToExistingCaseModal']; - mockCases.hooks.useCasesAddToExistingCaseModal = useCasesAddToExistingCaseModalMock; + mockCases.hooks.useCasesAddToExistingCaseModal = useCasesAddToExistingCaseModalMock; - const { getByTestId, findByRole } = render(); + const { getByTestId, findByRole } = render(); - fireEvent.click(await findByRole('button', { name: 'Actions' })); + fireEvent.click(await findByRole('button', { name: 'Actions' })); - fireEvent.click(getByTestId('add-to-case-button')); + fireEvent.click(getByTestId('add-to-case-button')); - expect(attachments).toEqual([ - { - alertId: mockAlertUuid, - index: '.internal.alerts-observability.metrics.alerts-*', - rule: { - id: ruleId, - name: ruleName, + expect(attachments).toEqual([ + { + alertId: mockAlertUuid, + index: '.internal.alerts-observability.metrics.alerts-*', + rule: { + id: mockRuleId, + name: mockRuleName, + }, + type: 'alert', }, - type: 'alert', - }, - ]); + ]); + }); + }); + }); + describe('Header Actions - Disabled', () => { + beforeEach(() => { + mockKibana(); + mockUseFetchRuleWithoutData(); + }); + it("should disable the 'View rule details' when the rule is not available/delete", async () => { + const { queryByTestId, findByRole } = render(); + fireEvent.click(await findByRole('button', { name: 'Actions' })); + expect(queryByTestId('view-rule-details-button')).toHaveAttribute('disabled'); }); }); }); diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx index edb2a1cb03c99..5da7046f210c2 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx @@ -95,7 +95,7 @@ export function HeaderActions({ alert }: HeaderActionsProps) { diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx index b82b45f1065e2..14afd4994b2be 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx @@ -248,7 +248,11 @@ export function AlertActions({ isOpen={isPopoverOpen} panelPaddingSize="none" > - + diff --git a/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx b/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx index ab490670f63ee..d0fc1d01734f2 100644 --- a/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx +++ b/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx @@ -19,7 +19,15 @@ export default { const Template: ComponentStory = (props: CasesProps) => ; const defaultProps: CasesProps = { - permissions: { read: true, all: true, create: true, delete: true, push: true, update: true }, + permissions: { + read: true, + all: true, + create: true, + delete: true, + push: true, + update: true, + connectors: true, + }, }; export const CasesPageWithAllPermissions = Template.bind({}); @@ -34,5 +42,6 @@ CasesPageWithNoPermissions.args = { delete: false, push: false, update: false, + connectors: false, }, }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/common/group_by_field_selector.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/common/group_by_field_selector.tsx new file mode 100644 index 0000000000000..0733e682e9ba7 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/common/group_by_field_selector.tsx @@ -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 { + EuiComboBox, + EuiComboBoxOptionOption, + EuiFlexItem, + EuiFormRow, + EuiIconTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ALL_VALUE } from '@kbn/slo-schema'; +import React from 'react'; +import { Controller, useFormContext } from 'react-hook-form'; +import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; +import { createOptionsFromFields } from '../../helpers/create_options'; +import { CreateSLOForm } from '../../types'; + +interface Props { + index?: string; +} +export function GroupByFieldSelector({ index }: Props) { + const { control, getFieldState } = useFormContext(); + const { isLoading, data: indexFields = [] } = useFetchIndexPatternFields(index); + const groupableFields = indexFields.filter((field) => field.aggregatable); + + const label = i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', { + defaultMessage: 'Select an optional field to partition by', + }); + + return ( + + + {i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', { + defaultMessage: 'Partition by', + })}{' '} + + + } + isInvalid={getFieldState('groupBy').invalid} + > + ( + { + if (selected.length) { + return field.onChange(selected[0].value); + } + + field.onChange(ALL_VALUE); + }} + options={createOptionsFromFields(groupableFields)} + selectedOptions={ + !!index && + !!field.value && + groupableFields.some((groupableField) => groupableField.name === field.value) + ? [{ value: field.value, label: field.value }] + : [] + } + singleSelection + /> + )} + /> + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.tsx index c33f5646ff8d9..954b89991f528 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.tsx @@ -36,7 +36,6 @@ export function QueryBuilder({ useKibana().services; const { control, getFieldState } = useFormContext(); - const { dataView } = useCreateDataView({ indexPatternString }); return ( diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx index c07ce5d132489..6d7ae6c012a2e 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx @@ -20,6 +20,7 @@ import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_inde import { createOptionsFromFields } from '../../helpers/create_options'; import { CreateSLOForm } from '../../types'; import { DataPreviewChart } from '../common/data_preview_chart'; +import { GroupByFieldSelector } from '../common/group_by_field_selector'; import { QueryBuilder } from '../common/query_builder'; import { IndexSelection } from '../custom_common/index_selection'; @@ -81,7 +82,7 @@ export function CustomKqlIndicatorTypeForm() { ? [{ value: field.value, label: field.value }] : [] } - singleSelection={{ asPlainText: true }} + singleSelection /> )} /> @@ -175,6 +176,8 @@ export function CustomKqlIndicatorTypeForm() { /> + + ); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx index e9a96fbc0c929..25de4568dc06c 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx @@ -27,15 +27,15 @@ import { DataPreviewChart } from '../common/data_preview_chart'; import { QueryBuilder } from '../common/query_builder'; import { IndexSelection } from '../custom_common/index_selection'; import { MetricIndicator } from './metric_indicator'; +import { GroupByFieldSelector } from '../common/group_by_field_selector'; export { NEW_CUSTOM_METRIC } from './metric_indicator'; export function CustomMetricIndicatorTypeForm() { const { control, watch, getFieldState } = useFormContext(); - const { isLoading, data: indexFields } = useFetchIndexPatternFields( - watch('indicator.params.index') - ); + const index = watch('indicator.params.index'); + const { isLoading, data: indexFields } = useFetchIndexPatternFields(index); const timestampFields = (indexFields ?? []).filter((field) => field.type === 'date'); return ( @@ -181,6 +181,8 @@ export function CustomMetricIndicatorTypeForm() { + + diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx index 323cd48a67cc0..56a867ab2e332 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx @@ -27,6 +27,7 @@ import { DataPreviewChart } from '../common/data_preview_chart'; import { QueryBuilder } from '../common/query_builder'; import { IndexSelection } from '../custom_common/index_selection'; import { HistogramIndicator } from './histogram_indicator'; +import { GroupByFieldSelector } from '../common/group_by_field_selector'; export function HistogramIndicatorTypeForm() { const { control, watch, getFieldState } = useFormContext(); @@ -163,6 +164,9 @@ export function HistogramIndicatorTypeForm() { + + + diff --git a/x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts b/x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts index d87dd10450f3d..a4ecec640e2df 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts +++ b/x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { CreateSLOInput, SLOWithSummaryResponse, UpdateSLOInput } from '@kbn/slo-schema'; +import { CreateSLOInput, SLOWithSummaryResponse, UpdateSLOInput } from '@kbn/slo-schema'; import { toDuration } from '../../../utils/slo/duration'; import { CreateSLOForm } from '../types'; diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx index 93b4e2802bc1c..f9c7cc3faeeb8 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx @@ -29,7 +29,7 @@ export interface Props { export function SloBadges({ activeAlerts, isLoading, rules, slo, onClickRuleBadge }: Props) { return ( - + {isLoading ? ( <> - + - + ({ name, template: { settings: { - auto_expand_replicas: '0-all', + auto_expand_replicas: '0-1', hidden: true, }, }, diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index fdcdde0197a04..14e5a26e7c7ff 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -65,7 +65,7 @@ const isLicenseAtLeastPlatinum = async (context: ObservabilityRequestHandlerCont const createSLORoute = createObservabilityServerRoute({ endpoint: 'POST /api/observability/slos 2023-10-31', options: { - tags: ['access:public', 'access:slo_write'], + tags: ['access:slo_write'], }, params: createSLOParamsSchema, handler: async ({ context, params, logger }) => { @@ -90,7 +90,7 @@ const createSLORoute = createObservabilityServerRoute({ const updateSLORoute = createObservabilityServerRoute({ endpoint: 'PUT /api/observability/slos/{id} 2023-10-31', options: { - tags: ['access:public', 'access:slo_write'], + tags: ['access:slo_write'], }, params: updateSLOParamsSchema, handler: async ({ context, params, logger }) => { @@ -116,7 +116,7 @@ const updateSLORoute = createObservabilityServerRoute({ const deleteSLORoute = createObservabilityServerRoute({ endpoint: 'DELETE /api/observability/slos/{id} 2023-10-31', options: { - tags: ['access:public', 'access:slo_write'], + tags: ['access:slo_write'], }, params: deleteSLOParamsSchema, handler: async ({ @@ -148,7 +148,7 @@ const deleteSLORoute = createObservabilityServerRoute({ const getSLORoute = createObservabilityServerRoute({ endpoint: 'GET /api/observability/slos/{id} 2023-10-31', options: { - tags: ['access:public', 'access:slo_read'], + tags: ['access:slo_read'], }, params: getSLOParamsSchema, handler: async ({ context, params }) => { @@ -173,7 +173,7 @@ const getSLORoute = createObservabilityServerRoute({ const enableSLORoute = createObservabilityServerRoute({ endpoint: 'POST /api/observability/slos/{id}/enable 2023-10-31', options: { - tags: ['access:public', 'access:slo_write'], + tags: ['access:slo_write'], }, params: manageSLOParamsSchema, handler: async ({ context, params, logger }) => { @@ -199,7 +199,7 @@ const enableSLORoute = createObservabilityServerRoute({ const disableSLORoute = createObservabilityServerRoute({ endpoint: 'POST /api/observability/slos/{id}/disable 2023-10-31', options: { - tags: ['access:public', 'access:slo_write'], + tags: ['access:slo_write'], }, params: manageSLOParamsSchema, handler: async ({ context, params, logger }) => { @@ -225,7 +225,7 @@ const disableSLORoute = createObservabilityServerRoute({ const findSLORoute = createObservabilityServerRoute({ endpoint: 'GET /api/observability/slos 2023-10-31', options: { - tags: ['access:public', 'access:slo_read'], + tags: ['access:slo_read'], }, params: findSLOParamsSchema, handler: async ({ context, params, logger }) => { diff --git a/x-pack/plugins/observability/server/services/slo/fetch_historical_summary.ts b/x-pack/plugins/observability/server/services/slo/fetch_historical_summary.ts index 75df72baa870f..095dbd8a64c38 100644 --- a/x-pack/plugins/observability/server/services/slo/fetch_historical_summary.ts +++ b/x-pack/plugins/observability/server/services/slo/fetch_historical_summary.ts @@ -25,11 +25,13 @@ export class FetchHistoricalSummary { const sloIds = params.list.map((slo) => slo.sloId); const sloList = await this.repository.findAllByIds(sloIds); - const list: SLOWithInstanceId[] = params.list.map(({ sloId, instanceId }) => ({ - sloId, - instanceId, - slo: sloList.find((slo) => slo.id === sloId)!, - })); + const list: SLOWithInstanceId[] = params.list + .filter(({ sloId }) => sloList.find((slo) => slo.id === sloId)) + .map(({ sloId, instanceId }) => ({ + sloId, + instanceId, + slo: sloList.find((slo) => slo.id === sloId)!, + })); const historicalSummary = await this.historicalSummaryClient.fetch(list); diff --git a/x-pack/plugins/observability/server/services/slo/slo_installer.ts b/x-pack/plugins/observability/server/services/slo/slo_installer.ts index b59612ca30253..d6e8b8295348f 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_installer.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_installer.ts @@ -37,7 +37,6 @@ export class DefaultSLOInstaller implements SLOInstaller { this.logger.error('Failed to install SLO common resources and summary transforms', { error, }); - throw error; } finally { this.isInstalling = false; clearTimeout(installTimeout); diff --git a/x-pack/plugins/observability/server/services/slo/summary_client.ts b/x-pack/plugins/observability/server/services/slo/summary_client.ts index b12a376be532a..3f799b2ca3473 100644 --- a/x-pack/plugins/observability/server/services/slo/summary_client.ts +++ b/x-pack/plugins/observability/server/services/slo/summary_client.ts @@ -20,8 +20,6 @@ import { DateRange, SLO, Summary } from '../../domain/models'; import { computeSLI, computeSummaryStatus, toErrorBudget } from '../../domain/services'; import { toDateRange } from '../../domain/services/date_range'; -// TODO: Change name of this service... -// It does compute a summary but from the rollup data. export interface SummaryClient { computeSummary(slo: SLO, instanceId?: string): Promise; } diff --git a/x-pack/plugins/observability/server/services/slo/summary_search_client.ts b/x-pack/plugins/observability/server/services/slo/summary_search_client.ts index c7d87ac8b322c..f2bfa1ed29df3 100644 --- a/x-pack/plugins/observability/server/services/slo/summary_search_client.ts +++ b/x-pack/plugins/observability/server/services/slo/summary_search_client.ts @@ -125,7 +125,7 @@ export class DefaultSummarySearchClient implements SummarySearchClient { page: pagination.page, results: finalResults.map((doc) => ({ id: doc._source!.slo.id, - instanceId: doc._source?.slo.instanceId ?? ALL_VALUE, + instanceId: doc._source!.slo.instanceId ?? ALL_VALUE, summary: { errorBudget: { initial: toHighPrecision(doc._source!.errorBudgetInitial), diff --git a/x-pack/plugins/observability_ai_assistant/common/types.ts b/x-pack/plugins/observability_ai_assistant/common/types.ts index 5cdabdceb5a2c..b98e255555904 100644 --- a/x-pack/plugins/observability_ai_assistant/common/types.ts +++ b/x-pack/plugins/observability_ai_assistant/common/types.ts @@ -5,14 +5,16 @@ * 2.0. */ -import { Serializable } from '@kbn/utility-types'; +import type { Serializable } from '@kbn/utility-types'; +import type { FromSchema } from 'json-schema-to-ts'; +import type { JSONSchema } from 'json-schema-to-ts'; +import React from 'react'; export enum MessageRole { System = 'system', Assistant = 'assistant', User = 'user', Function = 'function', - Event = 'event', Elastic = 'elastic', } @@ -21,13 +23,14 @@ export interface Message { message: { content?: string; name?: string; + event?: string; role: MessageRole; function_call?: { name: string; - args?: Serializable; + arguments?: string; trigger: MessageRole.Assistant | MessageRole.User | MessageRole.Elastic; }; - data?: Serializable; + data?: string; }; } @@ -46,6 +49,7 @@ export interface Conversation { labels: Record; numeric_labels: Record; namespace: string; + public: boolean; } export type ConversationRequestBase = Omit & { @@ -54,3 +58,60 @@ export type ConversationRequestBase = Omit; + +export interface ContextDefinition { + name: string; + description: string; +} + +interface FunctionResponse { + content?: Serializable; + data?: Serializable; +} + +interface FunctionOptions { + name: string; + description: string; + parameters: TParameters; + contexts: string[]; +} + +type RespondFunction< + TParameters extends CompatibleJSONSchema, + TResponse extends FunctionResponse +> = (options: { arguments: FromSchema }, signal: AbortSignal) => Promise; + +type RenderFunction = (options: { + response: TResponse; +}) => React.ReactNode; + +export interface FunctionDefinition { + options: FunctionOptions; + respond: (options: { arguments: any }, signal: AbortSignal) => Promise; + render?: RenderFunction; +} + +export type RegisterContextDefinition = (options: ContextDefinition) => void; + +export type RegisterFunctionDefinition = < + TParameters extends CompatibleJSONSchema, + TResponse extends FunctionResponse +>( + options: FunctionOptions, + respond: RespondFunction, + render?: RenderFunction +) => void; + +export type ContextRegistry = Map; +export type FunctionRegistry = Map; diff --git a/x-pack/plugins/observability_ai_assistant/kibana.jsonc b/x-pack/plugins/observability_ai_assistant/kibana.jsonc index 2faf2c7de57e9..ee32a40b29971 100644 --- a/x-pack/plugins/observability_ai_assistant/kibana.jsonc +++ b/x-pack/plugins/observability_ai_assistant/kibana.jsonc @@ -6,19 +6,9 @@ "id": "observabilityAIAssistant", "server": true, "browser": true, - "configPath": [ - "xpack", - "observabilityAIAssistant" - ], - "requiredPlugins": [ - "triggersActionsUi", - "actions", - "security", - "features" - ], - "requiredBundles": [ - "kibanaReact" - ], + "configPath": ["xpack", "observabilityAIAssistant"], + "requiredPlugins": ["triggersActionsUi", "actions", "security", "features", "observabilityShared"], + "requiredBundles": ["kibanaReact", "kibanaUtils"], "optionalPlugins": [], "extraPublicDirs": [] } diff --git a/x-pack/plugins/observability_ai_assistant/public/application.tsx b/x-pack/plugins/observability_ai_assistant/public/application.tsx new file mode 100644 index 0000000000000..869b805e3d3d9 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/application.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { EuiErrorBoundary } from '@elastic/eui'; +import type { CoreStart, CoreTheme } from '@kbn/core/public'; +import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { RouteRenderer, RouterProvider } from '@kbn/typed-react-router-config'; +import type { History } from 'history'; +import React from 'react'; +import type { Observable } from 'rxjs'; +import { ObservabilityAIAssistantProvider } from './context/observability_ai_assistant_provider'; +import { observabilityAIAssistantRouter } from './routes/config'; +import type { + ObservabilityAIAssistantPluginStartDependencies, + ObservabilityAIAssistantService, +} from './types'; + +export function Application({ + theme$, + history, + coreStart, + pluginsStart, + service, +}: { + theme$: Observable; + history: History; + coreStart: CoreStart; + pluginsStart: ObservabilityAIAssistantPluginStartDependencies; + service: ObservabilityAIAssistantService; +}) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/assistant_avatar.tsx b/x-pack/plugins/observability_ai_assistant/public/components/assistant_avatar.tsx index 4d653db359cd4..422bd42f16c3f 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/assistant_avatar.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/assistant_avatar.tsx @@ -4,10 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { ReactNode } from 'react'; export interface AssistantAvatarProps { - size: keyof typeof sizeMap; + size?: keyof typeof sizeMap; + children?: ReactNode; } export const sizeMap = { @@ -18,7 +19,7 @@ export const sizeMap = { xs: 16, }; -export function AssistantAvatar({ size }: AssistantAvatarProps) { +export function AssistantAvatar({ size = 's' }: AssistantAvatarProps) { return ( & { isExpanded: boolean } +) { + return ( + + {props.isExpanded + ? i18n.translate('xpack.observabilityAiAssistant.hideExpandConversationButton.hide', { + defaultMessage: 'Hide chats', + }) + : i18n.translate('xpack.observabilityAiAssistant.hideExpandConversationButton.show', { + defaultMessage: 'Show chats', + })} + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/buttons/new_chat_button.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/new_chat_button.stories.tsx new file mode 100644 index 0000000000000..f4e0cae677ef0 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/new_chat_button.stories.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; +import { NewChatButton as Component } from './new_chat_button'; + +const meta: ComponentMeta = { + component: Component, + title: 'app/Atoms/NewChatButton', +}; + +export default meta; + +export const NewChatButton: ComponentStoryObj = { + args: {}, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/buttons/new_chat_button.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/new_chat_button.tsx new file mode 100644 index 0000000000000..453c8da351119 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/new_chat_button.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export function NewChatButton(props: React.ComponentProps) { + return ( + + {i18n.translate('xpack.observabilityAiAssistant.newChatButton', { + defaultMessage: 'New chat', + })} + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/buttons/regenerate_response_button.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/regenerate_response_button.stories.tsx new file mode 100644 index 0000000000000..bfbb506a9e90a --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/regenerate_response_button.stories.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; +import { RegenerateResponseButton as Component } from './regenerate_response_button'; + +const meta: ComponentMeta = { + component: Component, + title: 'app/Atoms/RegenerateResponseButton', +}; + +export default meta; + +export const RegenerateResponseButton: ComponentStoryObj = { + args: {}, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/regenerate_response_button.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/regenerate_response_button.tsx similarity index 91% rename from x-pack/plugins/observability_ai_assistant/public/components/regenerate_response_button.tsx rename to x-pack/plugins/observability_ai_assistant/public/components/buttons/regenerate_response_button.tsx index 922f3c34a302b..b0439c854e2ca 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/regenerate_response_button.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/regenerate_response_button.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; export function RegenerateResponseButton(props: Partial) { return ( - + {i18n.translate('xpack.observabilityAiAssistant.regenerateResponseButtonLabel', { defaultMessage: 'Regenerate', })} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/buttons/start_chat_button.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/start_chat_button.stories.tsx new file mode 100644 index 0000000000000..de9116900b61b --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/start_chat_button.stories.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; +import { StartChatButton as Component } from './start_chat_button'; + +const meta: ComponentMeta = { + component: Component, + title: 'app/Atoms/StartChatButton', +}; + +export default meta; + +export const StartChatButton: ComponentStoryObj = { + args: {}, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/start_chat_button.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/start_chat_button.tsx similarity index 81% rename from x-pack/plugins/observability_ai_assistant/public/components/start_chat_button.tsx rename to x-pack/plugins/observability_ai_assistant/public/components/buttons/start_chat_button.tsx index b40586c5cfe35..dedbc827af4b2 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/start_chat_button.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/start_chat_button.tsx @@ -5,10 +5,10 @@ * 2.0. */ import React from 'react'; -import { EuiButton, EuiButtonProps } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -export function StartChatButton(props: Partial) { +export function StartChatButton(props: React.ComponentProps) { return ( {i18n.translate('xpack.observabilityAiAssistant.insight.response.startChat', { diff --git a/x-pack/plugins/observability_ai_assistant/public/components/buttons/stop_generating_button.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/stop_generating_button.stories.tsx new file mode 100644 index 0000000000000..acf27b4a01274 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/stop_generating_button.stories.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; +import { StopGeneratingButton as Component } from './stop_generating_button'; + +const meta: ComponentMeta = { + component: Component, + title: 'app/Atoms/StopGeneratingButton', +}; + +export default meta; + +export const StopGeneratingButton: ComponentStoryObj = { + args: {}, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/buttons/stop_generating_button.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/stop_generating_button.tsx new file mode 100644 index 0000000000000..3008ee884ae64 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/stop_generating_button.tsx @@ -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 { EuiButtonEmpty, EuiButtonEmptyProps } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +export function StopGeneratingButton(props: Partial) { + return ( + + {i18n.translate('xpack.observabilityAiAssistant.stopGeneratingButtonLabel', { + defaultMessage: 'Stop generating', + })} + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.stories.tsx new file mode 100644 index 0000000000000..befacf1ae3912 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.stories.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ComponentStory } from '@storybook/react'; +import React from 'react'; +import { Observable } from 'rxjs'; +import { getSystemMessage } from '../../service/get_system_message'; +import { ObservabilityAIAssistantService } from '../../types'; +import { ChatBody as Component } from './chat_body'; + +export default { + component: Component, + title: 'app/Organisms/ChatBody', +}; + +type ChatBodyProps = React.ComponentProps; + +const Template: ComponentStory = (props: ChatBodyProps) => { + return ( +
+ +
+ ); +}; + +const defaultProps: ChatBodyProps = { + title: 'My Conversation', + messages: [getSystemMessage()], + connectors: { + connectors: [ + { + id: 'foo', + referencedByCount: 1, + actionTypeId: 'foo', + name: 'GPT-v8-ultra', + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + }, + ], + loading: false, + error: undefined, + selectedConnector: 'foo', + selectConnector: () => {}, + }, + connectorsManagementHref: '', + currentUser: { + username: 'elastic', + }, + service: { + chat: () => { + return new Observable(); + }, + } as unknown as ObservabilityAIAssistantService, + onChatUpdate: () => {}, + onChatComplete: () => {}, +}; + +export const ChatBody = Template.bind({}); +ChatBody.args = defaultProps; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx new file mode 100644 index 0000000000000..d7deb33791cf5 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiLoadingSpinner, + EuiPanel, + EuiSpacer, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/css'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import React from 'react'; +import type { Message } from '../../../common/types'; +import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors'; +import { useTimeline } from '../../hooks/use_timeline'; +import { ObservabilityAIAssistantService } from '../../types'; +import { HideExpandConversationListButton } from '../buttons/hide_expand_conversation_list_button'; +import { MissingCredentialsCallout } from '../missing_credentials_callout'; +import { ChatHeader } from './chat_header'; +import { ChatPromptEditor } from './chat_prompt_editor'; +import { ChatTimeline } from './chat_timeline'; + +const containerClassName = css` + max-height: 100%; +`; + +const timelineClassName = css` + overflow-y: auto; +`; + +const loadingSpinnerContainerClassName = css` + align-self: center; +`; + +export function ChatBody({ + title, + messages, + connectors, + currentUser, + service, + connectorsManagementHref, + isConversationListExpanded, + onToggleExpandConversationList, + onChatUpdate, + onChatComplete, +}: { + title: string; + messages: Message[]; + connectors: UseGenAIConnectorsResult; + currentUser?: Pick; + service: ObservabilityAIAssistantService; + connectorsManagementHref: string; + isConversationListExpanded?: boolean; + onToggleExpandConversationList?: () => void; + onChatUpdate: (messages: Message[]) => void; + onChatComplete: (messages: Message[]) => void; +}) { + const { euiTheme } = useEuiTheme(); + + const timeline = useTimeline({ + messages, + connectors, + currentUser, + service, + onChatUpdate, + onChatComplete, + }); + + let footer: React.ReactNode; + + if (connectors.loading || connectors.connectors?.length === 0) { + footer = ( + <> + + {connectors.connectors?.length === 0 ? ( + + ) : ( + + + + )} + + ); + } else { + footer = ( + <> + + + + + + + + + + + + + + + ); + } + + return ( + + + {onToggleExpandConversationList ? ( + + + + ) : null} + + + + + + + + + + {footer} + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.stories.tsx new file mode 100644 index 0000000000000..a3c273a5d4251 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.stories.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ComponentStory } from '@storybook/react'; +import React from 'react'; +import { getSystemMessage } from '../../service/get_system_message'; +import { KibanaReactStorybookDecorator } from '../../utils/storybook_decorator'; +import { ChatFlyout as Component } from './chat_flyout'; + +export default { + component: Component, + title: 'app/Organisms/ChatFlyout', + decorators: [KibanaReactStorybookDecorator], +}; + +type ChatFlyoutProps = React.ComponentProps; + +const Template: ComponentStory = (props: ChatFlyoutProps) => { + return ( +
+ +
+ ); +}; + +const defaultProps: ChatFlyoutProps = { + isOpen: true, + title: 'How is this working', + messages: [getSystemMessage()], + onClose: () => {}, +}; + +export const ChatFlyout = Template.bind({}); +ChatFlyout.args = defaultProps; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx new file mode 100644 index 0000000000000..d2595354a4581 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiFlexGroup, EuiFlexItem, EuiFlyout } from '@elastic/eui'; +import React, { useState } from 'react'; +import type { Message } from '../../../common/types'; +import { useCurrentUser } from '../../hooks/use_current_user'; +import { useGenAIConnectors } from '../../hooks/use_genai_connectors'; +import { useKibana } from '../../hooks/use_kibana'; +import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant'; +import { getConnectorsManagementHref } from '../../utils/get_connectors_management_href'; +import { ChatBody } from './chat_body'; + +export function ChatFlyout({ + title, + messages, + isOpen, + onClose, +}: { + title: string; + messages: Message[]; + isOpen: boolean; + onClose: () => void; +}) { + const connectors = useGenAIConnectors(); + + const currentUser = useCurrentUser(); + + const { + services: { http }, + } = useKibana(); + + const [isConversationListExpanded, setIsConversationListExpanded] = useState(false); + + const service = useObservabilityAIAssistant(); + + return isOpen ? ( + + + + + setIsConversationListExpanded(!isConversationListExpanded) + } + onChatComplete={() => {}} + onChatUpdate={() => {}} + /> + + + + ) : null; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.stories.tsx new file mode 100644 index 0000000000000..ff21b7f103db3 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.stories.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; +import { FindActionResult } from '@kbn/actions-plugin/server'; +import { EuiPanel } from '@elastic/eui'; +import { ChatHeader as Component } from './chat_header'; + +const meta: ComponentMeta = { + component: Component, + title: 'app/Molecules/ChatHeader', +}; + +export default meta; + +export const ChatHeaderLoaded: ComponentStoryObj = { + args: { + title: 'My conversation', + connectors: { + loading: false, + selectedConnector: 'gpt-4', + connectors: [ + { id: 'gpt-4', name: 'OpenAI GPT-4' }, + { id: 'gpt-3.5-turbo', name: 'OpenAI GPT-3.5 Turbo' }, + ] as FindActionResult[], + selectConnector: () => {}, + }, + }, + render: (props) => { + return ( + + + + ); + }, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.tsx new file mode 100644 index 0000000000000..e5457b6c679e1 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiTitle, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/css'; +import { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors'; +import { AssistantAvatar } from '../assistant_avatar'; +import { ConnectorSelectorBase } from '../connector_selector/connector_selector_base'; +import { EMPTY_CONVERSATION_TITLE } from '../../i18n'; + +export function ChatHeader({ + title, + connectors, +}: { + title: string; + connectors: UseGenAIConnectorsResult; +}) { + const hasTitle = !!title; + + const displayedTitle = title || EMPTY_CONVERSATION_TITLE; + + const theme = useEuiTheme(); + + return ( + + + + + + + + +

{displayedTitle}

+
+
+ + + +
+
+
+ ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item.tsx new file mode 100644 index 0000000000000..ef17ba34ca780 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item.tsx @@ -0,0 +1,195 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiButtonIcon, + EuiComment, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, +} from '@elastic/eui'; +import { css } from '@emotion/css'; +import { i18n } from '@kbn/i18n'; +import React, { useState } from 'react'; +import { MessageRole } from '../../../common/types'; +import { Feedback, FeedbackButtons } from '../feedback_buttons'; +import { MessagePanel } from '../message_panel/message_panel'; +import { MessageText } from '../message_panel/message_text'; +import { RegenerateResponseButton } from '../buttons/regenerate_response_button'; +import { StopGeneratingButton } from '../buttons/stop_generating_button'; +import { ChatItemAvatar } from './chat_item_avatar'; +import { ChatItemTitle } from './chat_item_title'; +import { ChatTimelineItem } from './chat_timeline'; + +export interface ChatItemAction { + id: string; + label: string; + icon?: string; + handler: () => void; +} + +export interface ChatItemProps extends ChatTimelineItem { + onEditSubmit: (content: string) => void; + onFeedbackClick: (feedback: Feedback) => void; + onRegenerateClick: () => void; + onStopGeneratingClick: () => void; +} + +const euiCommentClassName = css` + .euiCommentEvent__headerEvent { + flex-grow: 1; + } + + > div:last-child { + overflow: hidden; + } +`; + +export function ChatItem({ + title, + content, + canEdit, + canGiveFeedback, + canRegenerate, + role, + loading, + error, + currentUser, + onEditSubmit, + onRegenerateClick, + onStopGeneratingClick, + onFeedbackClick, +}: ChatItemProps) { + const [isActionsPopoverOpen, setIsActionsPopover] = useState(false); + + const handleClickActions = () => { + setIsActionsPopover(!isActionsPopoverOpen); + }; + + const [_, setEditing] = useState(false); + + const actions: ChatItemAction[] = canEdit + ? [ + { + id: 'edit', + label: i18n.translate('xpack.observabilityAiAssistant.chatTimeline.actions.editMessage', { + defaultMessage: 'Edit message', + }), + handler: () => { + setEditing(false); + setIsActionsPopover(false); + }, + }, + ] + : []; + + let controls: React.ReactNode; + + const displayFeedback = !error && canGiveFeedback; + const displayRegenerate = !loading && canRegenerate; + + if (loading) { + controls = ; + } else if (displayFeedback || displayRegenerate) { + controls = ( + + {displayFeedback ? ( + + + + ) : null} + {displayRegenerate ? ( + + + + ) : null} + + ); + } + + return ( + + } + panelPaddingSize="s" + closePopover={handleClickActions} + isOpen={isActionsPopoverOpen} + > + ( + + {label} + + ))} + /> + + ) : null + } + title={title} + /> + } + className={euiCommentClassName} + timelineAvatar={ + + } + username={getRoleTranslation(role)} + > + {content || error || controls ? ( + : null + } + error={error} + controls={controls} + /> + ) : null} + + ); +} + +const getRoleTranslation = (role: MessageRole) => { + if (role === MessageRole.User) { + return i18n.translate('xpack.observabilityAiAssistant.chatTimeline.messages.user.label', { + defaultMessage: 'You', + }); + } + + if (role === MessageRole.System) { + return i18n.translate('xpack.observabilityAiAssistant.chatTimeline.messages.system.label', { + defaultMessage: 'System', + }); + } + + return i18n.translate( + 'xpack.observabilityAiAssistant.chatTimeline.messages.elasticAssistant.label', + { + defaultMessage: 'Elastic Assistant', + } + ); +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_avatar.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_avatar.tsx new file mode 100644 index 0000000000000..d04f818bb204c --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_avatar.tsx @@ -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 React from 'react'; +import { UserAvatar } from '@kbn/user-profile-components'; +import { EuiAvatar, EuiLoadingSpinner } from '@elastic/eui'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import { AssistantAvatar } from '../assistant_avatar'; +import { MessageRole } from '../../../common/types'; + +interface ChatAvatarProps { + currentUser?: Pick | undefined; + role: MessageRole; + loading: boolean; +} + +export function ChatItemAvatar({ currentUser, role, loading }: ChatAvatarProps) { + const isLoading = loading || !currentUser; + + if (isLoading) { + return ; + } + + switch (role) { + case MessageRole.User: + return ; + + case MessageRole.Assistant: + case MessageRole.Elastic: + case MessageRole.Function: + return ; + + case MessageRole.System: + return ; + + default: + return null; + } +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_title.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_title.tsx new file mode 100644 index 0000000000000..2749ef3635f40 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_title.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 { euiThemeVars } from '@kbn/ui-theme'; +import React, { ReactNode } from 'react'; + +interface ChatItemTitleProps { + actionsTrigger?: ReactNode; + title: string; +} + +export function ChatItemTitle({ actionsTrigger, title }: ChatItemTitleProps) { + return ( + <> + {title} + {actionsTrigger ? ( +
+ {actionsTrigger} +
+ ) : null} + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.stories.tsx new file mode 100644 index 0000000000000..eeb01da9b7d04 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.stories.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { ComponentStory } from '@storybook/react'; +import { ChatPromptEditor as Component, ChatPromptEditorProps } from './chat_prompt_editor'; +import { KibanaReactStorybookDecorator } from '../../utils/storybook_decorator'; + +/* + JSON Schema validation in the ChatPromptEditor compponent does not work + when rendering the component from within Storybook. + +*/ +export default { + component: Component, + title: 'app/Molecules/ChatPromptEditor', + argTypes: {}, + parameters: { + backgrounds: { + default: 'white', + values: [{ name: 'white', value: '#fff' }], + }, + }, + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: ChatPromptEditorProps) => { + return ; +}; + +const defaultProps = {}; + +export const ChatPromptEditor = Template.bind({}); +ChatPromptEditor.args = defaultProps; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.tsx new file mode 100644 index 0000000000000..a49da7665c9a2 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.tsx @@ -0,0 +1,219 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, useEffect, useRef, useState } from 'react'; +import { + EuiButtonIcon, + EuiButtonEmpty, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiPanel, + keys, +} from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant'; +import { useJsonEditorModel } from '../../hooks/use_json_editor_model'; +import { type Message, MessageRole } from '../../../common'; +import type { FunctionDefinition } from '../../../common/types'; +import { FunctionListPopover } from './function_list_popover'; + +export interface ChatPromptEditorProps { + disabled: boolean; + loading: boolean; + onSubmit: (message: Message) => Promise; +} + +export function ChatPromptEditor({ onSubmit, disabled, loading }: ChatPromptEditorProps) { + const { getFunctions } = useObservabilityAIAssistant(); + const functions = getFunctions(); + + const [prompt, setPrompt] = useState(''); + const [functionPayload, setFunctionPayload] = useState(''); + const [selectedFunction, setSelectedFunction] = useState(); + + const { model, initialJsonString } = useJsonEditorModel(selectedFunction); + + const ref = useRef(null); + + useEffect(() => { + setFunctionPayload(initialJsonString); + }, [initialJsonString, selectedFunction]); + + const handleChange = (event: React.ChangeEvent) => { + setPrompt(event.currentTarget.value); + }; + + const handleChangeFunctionPayload = (params: string) => { + setFunctionPayload(params); + }; + + const handleClearSelection = () => { + setSelectedFunction(undefined); + setFunctionPayload(''); + }; + + const handleSubmit = useCallback(async () => { + const currentPrompt = prompt; + const currentPayload = functionPayload; + + setPrompt(''); + setFunctionPayload(undefined); + + try { + if (selectedFunction) { + await onSubmit({ + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Function, + function_call: { + name: selectedFunction.options.name, + trigger: MessageRole.User, + arguments: currentPayload, + }, + }, + }); + } else { + await onSubmit({ + '@timestamp': new Date().toISOString(), + message: { role: MessageRole.User, content: currentPrompt }, + }); + setPrompt(''); + } + } catch (_) { + setPrompt(currentPrompt); + } + }, [functionPayload, onSubmit, prompt, selectedFunction]); + + useEffect(() => { + const keyboardListener = (event: KeyboardEvent) => { + if (event.key === keys.ENTER) { + handleSubmit(); + } + }; + + window.addEventListener('keyup', keyboardListener); + + return () => { + window.removeEventListener('keyup', keyboardListener); + }; + }, [handleSubmit]); + + useEffect(() => { + if (ref.current) { + ref.current.focus(); + } + }); + + return ( + + + + + + + + + + {selectedFunction ? ( + + {i18n.translate('xpack.observabilityAiAssistant.prompt.emptySelection', { + defaultMessage: 'Empty selection', + })} + + ) : null} + + + + + {selectedFunction ? ( + + + + ) : ( + + )} + + + + + + + + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.stories.tsx new file mode 100644 index 0000000000000..625f6ba28d141 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.stories.tsx @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { ComponentStory } from '@storybook/react'; +import React, { ComponentProps, useState } from 'react'; +import { MessageRole } from '../../../common'; +import { + buildAssistantChatItem, + buildChatInitItem, + buildFunctionChatItem, + buildUserChatItem, +} from '../../utils/builders'; +import { ChatTimeline as Component, ChatTimelineProps } from './chat_timeline'; + +export default { + component: Component, + title: 'app/Organisms/ChatTimeline', + parameters: { + backgrounds: { + default: 'white', + values: [{ name: 'white', value: '#fff' }], + }, + }, + argTypes: {}, +}; + +const Template: ComponentStory = (props: ChatTimelineProps) => { + const [count, setCount] = useState(props.items.length - 1); + + return ( + <> + index <= count)} /> + + + + setCount(count >= 0 && count < props.items.length - 1 ? count + 1 : 0)} + > + Add message + + + ); +}; + +const defaultProps: ComponentProps = { + items: [ + buildChatInitItem(), + buildUserChatItem(), + buildAssistantChatItem(), + buildUserChatItem({ content: 'How does it work?' }), + buildAssistantChatItem({ + content: `The way functions work depends on whether we are talking about mathematical functions or programming functions. Let's explore both: + + Mathematical Functions: + In mathematics, a function maps input values to corresponding output values based on a specific rule or expression. The general process of how a mathematical function works can be summarized as follows: + Step 1: Input - You provide an input value to the function, denoted as 'x' in the notation f(x). This value represents the independent variable. + + Step 2: Processing - The function takes the input value and applies a specific rule or algorithm to it. This rule is defined by the function itself and varies depending on the function's expression. + + Step 3: Output - After processing the input, the function produces an output value, denoted as 'f(x)' or 'y'. This output represents the dependent variable and is the result of applying the function's rule to the input. + + Step 4: Uniqueness - A well-defined mathematical function ensures that each input value corresponds to exactly one output value. In other words, the function should yield the same output for the same input whenever it is called.`, + }), + buildUserChatItem({ + content: 'Can you execute a function?', + }), + buildAssistantChatItem({ + content: 'Sure, I can do that.', + title: 'suggested a function', + function_call: { + name: 'a_function', + arguments: '{ "foo": "bar" }', + trigger: MessageRole.Assistant, + }, + canEdit: true, + }), + buildFunctionChatItem({ + content: '{ "message": "The arguments are wrong" }', + error: new Error(), + canRegenerate: false, + }), + buildAssistantChatItem({ + content: '', + title: 'suggested a function', + function_call: { + name: 'a_function', + arguments: '{ "bar": "foo" }', + trigger: MessageRole.Assistant, + }, + canEdit: true, + }), + buildFunctionChatItem({ + content: '', + title: 'are executing a function', + loading: true, + }), + ], + onEdit: () => {}, + onFeedback: () => {}, + onRegenerate: () => {}, + onStopGenerating: () => {}, +}; + +export const ChatTimeline = Template.bind({}); +ChatTimeline.args = defaultProps; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.tsx new file mode 100644 index 0000000000000..cdfbeecc7a56a --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCommentList } from '@elastic/eui'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import React from 'react'; +import type { Message } from '../../../common'; +import type { Feedback } from '../feedback_buttons'; +import { ChatItem } from './chat_item'; + +export interface ChatTimelineItem + extends Pick { + id: string; + title: string; + loading: boolean; + error?: any; + canEdit: boolean; + canRegenerate: boolean; + canGiveFeedback: boolean; + currentUser?: Pick; +} + +export interface ChatTimelineProps { + items: ChatTimelineItem[]; + onEdit: (item: ChatTimelineItem, content: string) => void; + onFeedback: (item: ChatTimelineItem, feedback: Feedback) => void; + onRegenerate: (item: ChatTimelineItem) => void; + onStopGenerating: () => void; +} + +export function ChatTimeline({ + items = [], + onEdit, + onFeedback, + onRegenerate, + onStopGenerating, +}: ChatTimelineProps) { + return ( + + {items.map((item, index) => ( + { + onFeedback(item, feedback); + }} + onRegenerateClick={() => { + onRegenerate(item); + }} + onEditSubmit={(content) => { + onEdit(item, content); + }} + onStopGeneratingClick={onStopGenerating} + /> + ))} + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/conversation_list.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/conversation_list.stories.tsx new file mode 100644 index 0000000000000..9be78fea80228 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/conversation_list.stories.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { ComponentMeta, ComponentStoryObj } from '@storybook/react'; +import React from 'react'; +import { KibanaReactStorybookDecorator } from '../../utils/storybook_decorator'; +import { ConversationList as Component } from './conversation_list'; + +type ConversationListProps = React.ComponentProps; + +const meta: ComponentMeta = { + component: Component, + title: 'app/Organisms/ConversationList', + decorators: [KibanaReactStorybookDecorator], +}; + +export default meta; + +const Wrapper = (props: ConversationListProps) => { + return ( +
+ +
+ ); +}; + +export const ChatHeaderLoading: ComponentStoryObj = { + args: { + loading: true, + }, + render: Wrapper, +}; + +export const ChatHeaderError: ComponentStoryObj = { + args: { + error: new Error(), + }, + render: Wrapper, +}; + +export const ChatHeaderLoaded: ComponentStoryObj = { + args: { + loading: false, + selected: '', + conversations: [ + { + id: '', + label: 'New conversation', + }, + { + id: 'first', + label: 'My first conversation', + href: '/my-first-conversation', + }, + { + id: 'second', + label: 'My second conversation', + href: '/my-second-conversation', + }, + ], + }, + render: Wrapper, +}; + +export const ChatHeaderEmpty: ComponentStoryObj = { + args: { + loading: false, + selected: '', + conversations: [], + }, + render: Wrapper, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/conversation_list.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/conversation_list.tsx new file mode 100644 index 0000000000000..27b1ff23d5c06 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/conversation_list.tsx @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiListGroup, + EuiListGroupItem, + EuiLoadingSpinner, + EuiPanel, + EuiText, +} from '@elastic/eui'; +import { css } from '@emotion/css'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { NewChatButton } from '../buttons/new_chat_button'; + +const containerClassName = css` + height: 100%; +`; + +const titleClassName = css` + text-transform: uppercase; +`; + +export function ConversationList({ + selected, + onClickNewChat, + loading, + error, + conversations, + onClickDeleteConversation, +}: { + selected: string; + onClickConversation: (conversationId: string) => void; + onClickNewChat: () => void; + loading: boolean; + error?: any; + conversations?: Array<{ id: string; label: string; href?: string }>; + onClickDeleteConversation: (id: string) => void; +}) { + return ( + + + + + + + + + + + {i18n.translate('xpack.observabilityAiAssistant.conversationList.title', { + defaultMessage: 'Previously', + })} + + + + {loading ? ( + + + + ) : null} + + + + {error ? ( + + + + + + + + + {i18n.translate( + 'xpack.observabilityAiAssistant.conversationList.errorMessage', + { + defaultMessage: 'Failed to load', + } + )} + + + + + + ) : null} + {conversations?.length ? ( + + + {conversations?.map((conversation) => ( + { + onClickDeleteConversation(conversation.id); + }, + } + : undefined + } + /> + ))} + + + ) : null} + + {!loading && !error && !conversations?.length ? ( + + + {i18n.translate( + 'xpack.observabilityAiAssistant.conversationList.noConversations', + { + defaultMessage: 'No conversations', + } + )} + + + ) : null} + + + + + + + + + + + + + + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/function_list_popover.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/function_list_popover.stories.tsx new file mode 100644 index 0000000000000..d4163fabaaa9a --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/function_list_popover.stories.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ComponentStory } from '@storybook/react'; +import React from 'react'; +import { KibanaReactStorybookDecorator } from '../../utils/storybook_decorator'; +import { FunctionListPopover as Component } from './function_list_popover'; + +export default { + component: Component, + title: 'app/Organisms/FunctionListPopover', + decorators: [KibanaReactStorybookDecorator], +}; + +type FunctionListPopover = React.ComponentProps; + +const Template: ComponentStory = (props: FunctionListPopover) => { + return ; +}; + +const defaultProps: FunctionListPopover = { + functions: [], + onSelectFunction: () => {}, +}; + +export const ConversationList = Template.bind({}); +ConversationList.args = defaultProps; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/function_list_popover.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/function_list_popover.tsx new file mode 100644 index 0000000000000..9e59167c84713 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/function_list_popover.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; +import { + EuiButtonEmpty, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiPopover, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FunctionDefinition } from '../../../common/types'; + +export function FunctionListPopover({ + functions, + selectedFunction, + onSelectFunction, +}: { + functions: FunctionDefinition[]; + selectedFunction?: FunctionDefinition; + onSelectFunction: (func: FunctionDefinition) => void; +}) { + const [isFunctionListOpen, setIsFunctionListOpen] = useState(false); + + const handleClickFunctionList = () => { + setIsFunctionListOpen(!isFunctionListOpen); + }; + + const handleSelectFunction = (func: FunctionDefinition) => { + setIsFunctionListOpen(false); + onSelectFunction(func); + }; + + useEffect(() => { + const keyboardListener = (event: KeyboardEvent) => { + if (event.shiftKey && event.code === 'Digit4') { + setIsFunctionListOpen(true); + } + }; + + window.addEventListener('keyup', keyboardListener); + + return () => { + window.removeEventListener('keyup', keyboardListener); + }; + }, []); + + return ( + + {selectedFunction + ? selectedFunction.options.name + : i18n.translate('xpack.observabilityAiAssistant.prompt.callFunction', { + defaultMessage: 'Call function', + })} + + } + closePopover={handleClickFunctionList} + panelPaddingSize="none" + isOpen={isFunctionListOpen} + > + + {functions.map((func) => ( + handleSelectFunction(func)}> + +

+ {func.options.name} +

+
+ + +

{func.options.description}

+
+
+ ))} +
+
+ ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.stories.tsx index 904c2ed8a4fcd..4a1a882c22692 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.stories.tsx @@ -4,9 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import React from 'react'; import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; import { FindActionResult } from '@kbn/actions-plugin/server'; +import { ComponentProps } from 'react'; +import { EuiPanel } from '@elastic/eui'; import { ConnectorSelectorBase as Component } from './connector_selector_base'; const meta: ComponentMeta = { @@ -16,6 +18,14 @@ const meta: ComponentMeta = { export default meta; +const render = (props: ComponentProps) => { + return ( + + + + ); +}; + export const Loaded: ComponentStoryObj = { args: { loading: false, @@ -25,12 +35,14 @@ export const Loaded: ComponentStoryObj = { { id: 'gpt-3.5-turbo', name: 'OpenAI GPT-3.5 Turbo' }, ] as FindActionResult[], }, + render, }; export const Loading: ComponentStoryObj = { args: { loading: true, }, + render, }; export const Empty: ComponentStoryObj = { @@ -38,6 +50,7 @@ export const Empty: ComponentStoryObj = { loading: false, connectors: [], }, + render, }; export const FailedToLoad: ComponentStoryObj = { @@ -45,4 +58,5 @@ export const FailedToLoad: ComponentStoryObj = { loading: false, error: new Error('Failed to load connectors'), }, + render, }; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.tsx b/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.tsx index 0667cb66c8e3c..5d22119bd5010 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/connector_selector/connector_selector_base.tsx @@ -26,6 +26,7 @@ const wrapperClassName = css` border: none; box-shadow: none; background: none; + padding-left: 0; } `; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx index f474c20fd20c4..e34050bf19f46 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx @@ -4,26 +4,123 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import type { Message } from '../../../common/types'; -import { useChat } from '../../hooks/use_chat'; +import { AbortError } from '@kbn/kibana-utils-plugin/common'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import type { Subscription } from 'rxjs'; +import { MessageRole, type Message } from '../../../common/types'; import { useGenAIConnectors } from '../../hooks/use_genai_connectors'; +import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant'; +import type { PendingMessage } from '../../types'; +import { ChatFlyout } from '../chat/chat_flyout'; import { ConnectorSelectorBase } from '../connector_selector/connector_selector_base'; import { MessagePanel } from '../message_panel/message_panel'; import { MessageText } from '../message_panel/message_text'; +import { RegenerateResponseButton } from '../buttons/regenerate_response_button'; +import { StartChatButton } from '../buttons/start_chat_button'; +import { StopGeneratingButton } from '../buttons/stop_generating_button'; import { InsightBase } from './insight_base'; -import { InsightMissingCredentials } from './insight_missing_credentials'; +import { MissingCredentialsCallout } from '../missing_credentials_callout'; +import { getConnectorsManagementHref } from '../../utils/get_connectors_management_href'; -function ChatContent({ messages, connectorId }: { messages: Message[]; connectorId: string }) { - const chat = useChat({ messages, connectorId }); +function ChatContent({ + title, + messages, + connectorId, +}: { + title: string; + messages: Message[]; + connectorId: string; +}) { + const service = useObservabilityAIAssistant(); + + const [pendingMessage, setPendingMessage] = useState(); + const [loading, setLoading] = useState(false); + const [subscription, setSubscription] = useState(); + + const reloadReply = useCallback(() => { + setLoading(true); + + const nextSubscription = service.chat({ messages, connectorId }).subscribe({ + next: (msg) => { + setPendingMessage(() => msg); + }, + complete: () => { + setLoading(false); + }, + }); + + setSubscription(nextSubscription); + }, [messages, connectorId, service]); + + useEffect(() => { + reloadReply(); + }, [reloadReply]); + + const [isOpen, setIsOpen] = useState(false); + + const displayedMessages = useMemo(() => { + return pendingMessage + ? messages.concat({ + '@timestamp': new Date().toISOString(), + message: { + ...pendingMessage.message, + }, + }) + : messages; + }, [pendingMessage, messages]); return ( - } - error={chat.error} - controls={null} - /> + <> + } + error={pendingMessage?.error} + controls={ + loading ? ( + { + subscription?.unsubscribe(); + setLoading(false); + setPendingMessage((prev) => ({ + message: { + role: MessageRole.Assistant, + ...prev?.message, + }, + aborted: true, + error: new AbortError(), + })); + }} + /> + ) : ( + + + { + reloadReply(); + }} + /> + + + { + setIsOpen(() => true); + }} + /> + + + ) + } + /> + { + setIsOpen(() => false); + }} + messages={displayedMessages} + /> + ); } @@ -39,14 +136,12 @@ export function Insight({ messages, title }: { messages: Message[]; title: strin let children: React.ReactNode = null; if (hasOpened && connectors.selectedConnector) { - children = ; + children = ( + + ); } else if (!connectors.loading && !connectors.connectors?.length) { children = ( - + ); } diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx index 45b184832e619..7a65f8e62756e 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.stories.tsx @@ -16,8 +16,8 @@ import { ConnectorSelectorBase } from '../connector_selector/connector_selector_ import { MessagePanel } from '../message_panel/message_panel'; import { MessageText } from '../message_panel/message_text'; import { FeedbackButtons } from '../feedback_buttons'; -import { RegenerateResponseButton } from '../regenerate_response_button'; -import { StartChatButton } from '../start_chat_button'; +import { RegenerateResponseButton } from '../buttons/regenerate_response_button'; +import { StartChatButton } from '../buttons/start_chat_button'; export default { component: Component, diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx index 47f0abb3e1ec1..b65553a7b7ec4 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx @@ -122,7 +122,9 @@ export function InsightBase({ onToggle={onToggle} > - {children} + + {children} + ); diff --git a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx index d58907b5766e5..2ab602e429ce1 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { EuiPanel } from '@elastic/eui'; import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; import React from 'react'; import { FeedbackButtons } from '../feedback_buttons'; @@ -18,12 +19,25 @@ const meta: ComponentMeta = { export default meta; export const ContentLoading: ComponentStoryObj = { + render: (props, context) => { + return ( + + + + ); + }, args: { body: ( + <> {props.body} {props.error ? ( <> @@ -51,6 +50,6 @@ export function MessagePanel(props: Props) { {props.controls} ) : null} - + ); } diff --git a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx index 897488d73f507..96cddb6a3b7ca 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx @@ -4,33 +4,37 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { v4 } from 'uuid'; -import React from 'react'; -import type { Node } from 'unist'; +import { EuiText } from '@elastic/eui'; import { css } from '@emotion/css'; -import type { Parent, Text } from 'mdast'; +import { euiThemeVars } from '@kbn/ui-theme'; +import classNames from 'classnames'; +import type { Code, InlineCode, Parent, Text } from 'mdast'; +import React from 'react'; import ReactMarkdown from 'react-markdown'; -import { EuiText } from '@elastic/eui'; +import type { Node } from 'unist'; +import { v4 } from 'uuid'; interface Props { content: string; loading: boolean; } +const ANIMATION_TIME = 1; + const cursorCss = css` @keyframes blink { 0% { - opacity: 1; + opacity: 0; } 50% { - opacity: 0; + opacity: 1; } 100% { - opacity: 1; + opacity: 0; } } - animation: blink 1s infinite; + animation: blink ${ANIMATION_TIME}s infinite; width: 10px; height: 16px; vertical-align: middle; @@ -38,7 +42,7 @@ const cursorCss = css` background: rgba(0, 0, 0, 0.25); `; -const cursor = ; +const Cursor = () => ; const CURSOR = `{{${v4()}}`; @@ -51,11 +55,11 @@ const loadingCursorPlugin = () => { }); } - if (node.type !== 'text') { + if (node.type !== 'text' && node.type !== 'inlineCode' && node.type !== 'code') { return; } - const textNode = node as Text; + const textNode = node as Text | InlineCode | Code; const indexOfCursor = textNode.value.indexOf(CURSOR); if (indexOfCursor === -1) { @@ -80,13 +84,22 @@ const loadingCursorPlugin = () => { }; export function MessageText(props: Props) { + const containerClassName = css` + overflow-wrap: break-word; + + code { + background: ${euiThemeVars.euiColorLightestShade}; + padding: 0 8px; + } + `; + return ( - + cursor, + cursor: Cursor, } as Record } > diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_missing_credentials.tsx b/x-pack/plugins/observability_ai_assistant/public/components/missing_credentials_callout.tsx similarity index 73% rename from x-pack/plugins/observability_ai_assistant/public/components/insight/insight_missing_credentials.tsx rename to x-pack/plugins/observability_ai_assistant/public/components/missing_credentials_callout.tsx index aab2dd87a3d08..20c8257b8721f 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_missing_credentials.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/missing_credentials_callout.tsx @@ -13,16 +13,16 @@ interface Props { connectorsManagementHref: string; } -export function InsightMissingCredentials(props: Props) { +export function MissingCredentialsCallout(props: Props) { return ( - {i18n.translate('xpack.observabilityAiAssistant.insight.missing.description', { + {i18n.translate('xpack.observabilityAiAssistant.missingCredentialsCallout.description', { defaultMessage: 'You haven’t authorised OpenAI in order to generate responses from the Elastic Assistant. Authorise the model in order to proceed.', })} @@ -30,7 +30,7 @@ export function InsightMissingCredentials(props: Props) { - {i18n.translate('xpack.observabilityAiAssistant.insight.missing.buttonLabel', { + {i18n.translate('xpack.observabilityAiAssistant.missingCredentialsCallout.buttonLabel', { defaultMessage: 'Connect Assistant', })} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/page_template.tsx b/x-pack/plugins/observability_ai_assistant/public/components/page_template.tsx new file mode 100644 index 0000000000000..dbbb2db235f19 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/components/page_template.tsx @@ -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 { css } from '@emotion/css'; +import React from 'react'; +import { useKibana } from '../hooks/use_kibana'; + +const pageSectionContentClassName = css` + width: 100%; + display: flex; + flex-grow: 1; + padding-top: 0; + padding-bottom: 0; +`; + +export function ObservabilityAIAssistantPageTemplate({ children }: { children: React.ReactNode }) { + const { + services: { + plugins: { + start: { observabilityShared }, + }, + }, + } = useKibana(); + + const PageTemplate = observabilityShared.navigation.PageTemplate; + + return ( + + {children} + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/elasticsearch.ts b/x-pack/plugins/observability_ai_assistant/public/functions/elasticsearch.ts new file mode 100644 index 0000000000000..154f1ac40c20a --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/functions/elasticsearch.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { Serializable } from '@kbn/utility-types'; +import type { RegisterFunctionDefinition } from '../../common/types'; +import type { ObservabilityAIAssistantService } from '../types'; + +export function registerElasticsearchFunction({ + service, + registerFunction, +}: { + service: ObservabilityAIAssistantService; + registerFunction: RegisterFunctionDefinition; +}) { + registerFunction( + { + name: 'elasticsearch', + contexts: ['core'], + description: 'Call Elasticsearch APIs on behalf of the user', + parameters: { + type: 'object', + properties: { + method: { + type: 'string', + description: 'The HTTP method of the Elasticsearch endpoint', + enum: ['GET', 'PUT', 'POST', 'DELETE', 'PATCH'] as const, + }, + path: { + type: 'string', + description: 'The path of the Elasticsearch endpoint, including query parameters', + }, + }, + required: ['method' as const, 'path' as const], + }, + }, + ({ arguments: { method, path, body } }, signal) => { + return service + .callApi(`POST /internal/observability_ai_assistant/functions/elasticsearch`, { + signal, + params: { + body: { + method, + path, + body, + }, + }, + }) + .then((response) => ({ content: response as Serializable })); + } + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/index.ts b/x-pack/plugins/observability_ai_assistant/public/functions/index.ts new file mode 100644 index 0000000000000..450793554f19e --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/functions/index.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 type { RegisterContextDefinition, RegisterFunctionDefinition } from '../../common/types'; +import type { ObservabilityAIAssistantService } from '../types'; +import { registerElasticsearchFunction } from './elasticsearch'; +import { registerRecallFunction } from './recall'; +import { registerSetupKbFunction } from './setup_kb'; +import { registerSummarisationFunction } from './summarise'; + +export function registerFunctions({ + registerFunction, + registerContext, + service, +}: { + registerFunction: RegisterFunctionDefinition; + registerContext: RegisterContextDefinition; + service: ObservabilityAIAssistantService; +}) { + registerContext({ + name: 'core', + description: + 'Core functions, like calling Elasticsearch APIs, storing embeddables for instructions or creating base visualisations.', + }); + + registerElasticsearchFunction({ service, registerFunction }); + registerSummarisationFunction({ service, registerFunction }); + registerRecallFunction({ service, registerFunction }); + registerSetupKbFunction({ service, registerFunction }); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/recall.ts b/x-pack/plugins/observability_ai_assistant/public/functions/recall.ts new file mode 100644 index 0000000000000..576eba182c659 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/functions/recall.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 type { Serializable } from '@kbn/utility-types'; +import type { RegisterFunctionDefinition } from '../../common/types'; +import type { ObservabilityAIAssistantService } from '../types'; + +export function registerRecallFunction({ + service, + registerFunction, +}: { + service: ObservabilityAIAssistantService; + registerFunction: RegisterFunctionDefinition; +}) { + registerFunction( + { + name: 'recall', + contexts: ['core'], + description: + 'Use this function to recall earlier learnings. Anything you will summarise can be retrieved again later via this function.', + parameters: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'The query for the semantic search', + }, + }, + required: ['query' as const], + }, + }, + ({ arguments: { query } }, signal) => { + return service + .callApi('POST /internal/observability_ai_assistant/functions/recall', { + params: { + body: { + query, + }, + }, + signal, + }) + .then((response) => ({ content: response as unknown as Serializable })); + } + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/setup_kb.ts b/x-pack/plugins/observability_ai_assistant/public/functions/setup_kb.ts new file mode 100644 index 0000000000000..9cb498e1a7793 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/functions/setup_kb.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Serializable } from '@kbn/utility-types'; +import type { RegisterFunctionDefinition } from '../../common/types'; +import type { ObservabilityAIAssistantService } from '../types'; + +export function registerSetupKbFunction({ + service, + registerFunction, +}: { + service: ObservabilityAIAssistantService; + registerFunction: RegisterFunctionDefinition; +}) { + registerFunction( + { + name: 'setup_kb', + contexts: ['core'], + description: + 'Use this function to set up the knowledge base. ONLY use this if you got an error from the recall or summarise function, or if the user has explicitly requested it. Note that it might take a while (e.g. ten minutes) until the knowledge base is available. Assume it will not be ready for the rest of the current conversation.', + parameters: { + type: 'object', + properties: {}, + }, + }, + ({}, signal) => { + return service + .callApi('POST /internal/observability_ai_assistant/functions/setup_kb', { + signal, + }) + .then((response) => ({ content: response as unknown as Serializable })); + } + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/summarise.ts b/x-pack/plugins/observability_ai_assistant/public/functions/summarise.ts new file mode 100644 index 0000000000000..723839fd6da6f --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/functions/summarise.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 type { RegisterFunctionDefinition } from '../../common/types'; +import type { ObservabilityAIAssistantService } from '../types'; + +export function registerSummarisationFunction({ + service, + registerFunction, +}: { + service: ObservabilityAIAssistantService; + registerFunction: RegisterFunctionDefinition; +}) { + registerFunction( + { + name: 'summarise', + contexts: ['core'], + description: + 'Use this function to summarise things learned from the conversation. You can score the learnings with a confidence metric, whether it is a correction on a previous learning. An embedding will be created that you can recall later with a semantic search. There is no need to ask the user for permission to store something you have learned, unless you do not feel confident.', + parameters: { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later.', + }, + text: { + type: 'string', + description: + 'A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search.', + }, + is_correction: { + type: 'boolean', + description: 'Whether this is a correction for a previous learning.', + }, + confidence: { + type: 'string', + description: 'How confident you are about this being a correct and useful learning', + enum: ['low' as const, 'medium' as const, 'high' as const], + }, + public: { + type: 'boolean', + description: + 'Whether this information is specific to the user, or generally applicable to any user of the product', + }, + }, + required: [ + 'id' as const, + 'text' as const, + 'is_correction' as const, + 'confidence' as const, + 'public' as const, + ], + }, + }, + ( + { arguments: { id, text, is_correction: isCorrection, confidence, public: isPublic } }, + signal + ) => { + return service + .callApi('POST /internal/observability_ai_assistant/functions/summarise', { + params: { + body: { + id, + text, + is_correction: isCorrection, + confidence, + public: isPublic, + }, + }, + signal, + }) + .then(() => ({ + content: { + message: `The document has been stored`, + }, + })); + } + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_conversations.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_conversations.ts new file mode 100644 index 0000000000000..d41fd4aac1b29 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_conversations.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 { uniqueId } from 'lodash'; +import { buildConversation } from '../../utils/builders'; + +export function useConversations() { + return [ + buildConversation({ + conversation: { + id: uniqueId(), + title: 'Investigation into Cart service degradation', + last_updated: '', + }, + }), + buildConversation({ + conversation: { + id: uniqueId(), + title: 'Why is database service responding with errors after I did rm -rf /postgres', + last_updated: '', + }, + }), + buildConversation({ + conversation: { + id: uniqueId(), + title: 'Why is database service responding with errors after I did rm -rf /postgres', + last_updated: '', + }, + }), + buildConversation({ + conversation: { + id: uniqueId(), + title: 'Why is database service responding with errors after I did rm -rf /postgres', + last_updated: '', + }, + }), + buildConversation({ + conversation: { + id: uniqueId(), + title: 'Why is database service responding with errors after I did rm -rf /postgres', + last_updated: '', + }, + }), + buildConversation({ + conversation: { + id: uniqueId(), + title: 'Why is database service responding with errors after I did rm -rf /postgres', + last_updated: '', + }, + }), + buildConversation({ + conversation: { + id: uniqueId(), + title: 'Why is database service responding with errors after I did rm -rf /postgres', + last_updated: '', + }, + }), + ]; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_current_user.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_current_user.ts new file mode 100644 index 0000000000000..cbeba226f3835 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_current_user.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export function useCurrentUser() { + return { + username: 'john_doe', + email: 'john.doe@example.com', + full_name: 'John Doe', + roles: ['user', 'editor'], + enabled: true, + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_genai_connectors.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_genai_connectors.ts new file mode 100644 index 0000000000000..29290d9b6b898 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_genai_connectors.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UseGenAIConnectorsResult } from '../use_genai_connectors'; + +export function useGenAIConnectors(): UseGenAIConnectorsResult { + return { + connectors: [], + loading: false, + error: undefined, + selectedConnector: 'foo', + selectConnector: (id: string) => {}, + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_kibana.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_kibana.ts new file mode 100644 index 0000000000000..41239c6e4af1a --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_kibana.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export function useKibana() { + return { + services: { + uiSettings: { + get: (setting: string) => { + if (setting === 'dateFormat') { + return 'MMM D, YYYY HH:mm'; + } + }, + }, + notifications: { + toasts: { + addSuccess: () => {}, + addError: () => {}, + }, + }, + }, + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_abortable_async.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_abortable_async.ts new file mode 100644 index 0000000000000..393d7cba2ae35 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_abortable_async.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { isPromise } from '@kbn/std'; +import { useEffect, useMemo, useRef, useState } from 'react'; + +interface State { + error?: Error; + value?: T; + loading: boolean; +} + +export type AbortableAsyncState = (T extends Promise + ? State + : State) & { refresh: () => void }; + +export function useAbortableAsync( + fn: ({}: { signal: AbortSignal }) => T, + deps: any[] +): AbortableAsyncState { + const controllerRef = useRef(new AbortController()); + + const [refreshId, setRefreshId] = useState(0); + + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + const [value, setValue] = useState(); + + useEffect(() => { + controllerRef.current.abort(); + + const controller = new AbortController(); + controllerRef.current = controller; + + try { + const response = fn({ signal: controller.signal }); + if (isPromise(response)) { + setLoading(true); + response + .then(setValue) + .catch((err) => { + setValue(undefined); + setError(err); + }) + .finally(() => setLoading(false)); + } else { + setError(undefined); + setValue(response); + } + } catch (err) { + setValue(undefined); + setError(err); + } finally { + setLoading(false); + } + + return () => { + controller.abort(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps.concat(refreshId)); + + return useMemo>(() => { + return { + error, + loading, + value, + refresh: () => { + setRefreshId((id) => id + 1); + }, + } as unknown as AbortableAsyncState; + }, [error, value, loading]); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.ts deleted file mode 100644 index eb61f592cd2f3..0000000000000 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.ts +++ /dev/null @@ -1,111 +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 { clone } from 'lodash'; -import { useEffect, useState } from 'react'; -import { concatMap, delay, of } from 'rxjs'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { i18n } from '@kbn/i18n'; -import type { Message } from '../../common/types'; -import { useObservabilityAIAssistant } from './use_observability_ai_assistant'; - -interface MessageResponse { - content?: string; - function_call?: { - name?: string; - args?: string; - }; -} - -export function useChat({ messages, connectorId }: { messages: Message[]; connectorId: string }): { - content?: string; - function_call?: { - name?: string; - args?: string; - }; - loading: boolean; - error?: Error; -} { - const assistant = useObservabilityAIAssistant(); - - const { - services: { notifications }, - } = useKibana(); - - const [response, setResponse] = useState(undefined); - - const [error, setError] = useState(undefined); - - const [loading, setLoading] = useState(false); - - useEffect(() => { - const controller = new AbortController(); - - setResponse(undefined); - setError(undefined); - setLoading(true); - - const partialResponse = { - content: '', - function_call: { - name: '', - args: '', - }, - }; - - assistant - .chat({ messages, connectorId, signal: controller.signal }) - .then((response$) => { - return new Promise((resolve, reject) => { - const subscription = response$ - .pipe(concatMap((value) => of(value).pipe(delay(50)))) - .subscribe({ - next: (chunk) => { - partialResponse.content += chunk.choices[0].delta.content ?? ''; - partialResponse.function_call.name += - chunk.choices[0].delta.function_call?.name ?? ''; - partialResponse.function_call.args += - chunk.choices[0].delta.function_call?.args ?? ''; - setResponse(clone(partialResponse)); - }, - error: (err) => { - reject(err); - }, - complete: () => { - resolve(); - }, - }); - - controller.signal.addEventListener('abort', () => { - subscription.unsubscribe(); - }); - }); - }) - .catch((err) => { - notifications?.showErrorDialog({ - title: i18n.translate('xpack.observabilityAiAssistant.failedToLoadChatTitle', { - defaultMessage: 'Failed to load chat', - }), - error: err, - }); - setError(err); - }) - .finally(() => { - setLoading(false); - }); - - return () => { - controller.abort(); - }; - }, [messages, connectorId, assistant, notifications]); - - return { - ...response, - error, - loading, - }; -} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_confirm_modal.tsx b/x-pack/plugins/observability_ai_assistant/public/hooks/use_confirm_modal.tsx new file mode 100644 index 0000000000000..e1cff9175be4c --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_confirm_modal.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { EuiConfirmModal } from '@elastic/eui'; +import { useState } from 'react'; + +export function useConfirmModal({ + title, + children, + confirmButtonText, +}: { + title: React.ReactNode; + children: React.ReactNode; + confirmButtonText: React.ReactNode; +}) { + const [element, setElement] = useState(undefined); + + const confirm = () => { + return new Promise((resolve) => { + setElement( + { + resolve(true); + setElement(undefined); + }} + onCancel={() => { + resolve(false); + setElement(undefined); + }} + confirmButtonText={confirmButtonText} + > + {children} + + ); + }); + }; + + return { + element, + confirm, + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversations.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversations.ts new file mode 100644 index 0000000000000..b94108264007c --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversations.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Conversation } from '../../common'; + +export function useConversations(): Conversation[] { + return []; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_current_user.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_current_user.ts new file mode 100644 index 0000000000000..8e8f437a87fb2 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_current_user.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import { useEffect, useState } from 'react'; +import { useObservabilityAIAssistant } from './use_observability_ai_assistant'; + +export function useCurrentUser() { + const service = useObservabilityAIAssistant(); + + const [user, setUser] = useState(); + + useEffect(() => { + const getCurrentUser = async () => { + try { + const authenticatedUser = await service.getCurrentUser(); + setUser(authenticatedUser); + } catch { + setUser(undefined); + } + }; + getCurrentUser(); + }, [service]); + + return user; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_genai_connectors.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_genai_connectors.ts index e8373e0124909..69b657e54661c 100644 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_genai_connectors.ts +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_genai_connectors.ts @@ -43,6 +43,13 @@ export function useGenAIConnectors(): UseGenAIConnectorsResult { }) .then((results) => { setConnectors(results); + setSelectedConnector((connectorId) => { + if (connectorId && results.findIndex((result) => result.id === connectorId) === -1) { + return ''; + } + return connectorId; + }); + setError(undefined); }) .catch((err) => { @@ -56,7 +63,7 @@ export function useGenAIConnectors(): UseGenAIConnectorsResult { return () => { controller.abort(); }; - }, [assistant]); + }, [assistant, setSelectedConnector]); return { connectors, diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_json_editor_model.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_json_editor_model.ts new file mode 100644 index 0000000000000..f04cdf68e9c3e --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_json_editor_model.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { useMemo } from 'react'; +import { monaco } from '@kbn/monaco'; +import { FunctionDefinition } from '../../common/types'; + +const { editor, languages, Uri } = monaco; + +const SCHEMA_URI = 'http://elastic.co/foo.json'; +const modelUri = Uri.parse(SCHEMA_URI); + +export const useJsonEditorModel = (functionDefinition?: FunctionDefinition) => { + return useMemo(() => { + if (!functionDefinition) { + return {}; + } + + const schema = { ...functionDefinition.options.parameters }; + + const initialJsonString = functionDefinition.options.parameters.properties + ? Object.keys(functionDefinition.options.parameters.properties).reduce( + (acc, curr, index, arr) => { + const val = `${acc} "${curr}": "",\n`; + return index === arr.length - 1 ? `${val}}` : val; + }, + '{\n' + ) + : ''; + + languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + schemas: [ + { + uri: SCHEMA_URI, + fileMatch: [String(modelUri)], + schema, + }, + ], + }); + + let model = editor.getModel(modelUri); + + if (model === null) { + model = editor.createModel(initialJsonString, 'json', modelUri); + } + + return { model, initialJsonString }; + }, [functionDefinition]); +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_kibana.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_kibana.ts new file mode 100644 index 0000000000000..03cdd5f5ac826 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_kibana.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart } from '@kbn/core/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ObservabilityAIAssistantPluginStartDependencies } from '../types'; + +export type StartServices = CoreStart & { + plugins: { start: ObservabilityAIAssistantPluginStartDependencies }; +} & TAdditionalServices & {}; +const useTypedKibana = () => + useKibana>(); + +export { useTypedKibana as useKibana }; diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant_params.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant_params.ts new file mode 100644 index 0000000000000..0fa7fb39ffc06 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant_params.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { type PathsOf, type TypeOf, useParams } from '@kbn/typed-react-router-config'; +import type { ObservabilityAIAssistantRoutes } from '../routes/config'; + +export function useObservabilityAIAssistantParams< + TPath extends PathsOf +>(path: TPath): TypeOf { + return useParams(path)! as TypeOf; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant_router.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant_router.ts new file mode 100644 index 0000000000000..160a4835d0ffb --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant_router.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PathsOf, TypeAsArgs, TypeOf } from '@kbn/typed-react-router-config'; +import { useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; +import { ObservabilityAIAssistantRouter, ObservabilityAIAssistantRoutes } from '../routes/config'; +import { observabilityAIAssistantRouter } from '../routes/config'; +import { useKibana } from './use_kibana'; + +interface StatefulObservabilityAIAssistantRouter extends ObservabilityAIAssistantRouter { + push>( + path: T, + ...params: TypeAsArgs> + ): void; + replace>( + path: T, + ...params: TypeAsArgs> + ): void; +} + +export function useObservabilityAIAssistantRouter(): StatefulObservabilityAIAssistantRouter { + const history = useHistory(); + + const { + services: { http }, + } = useKibana(); + + const link = (...args: any[]) => { + // @ts-expect-error + return observabilityAIAssistantRouter.link(...args); + }; + + return useMemo( + () => ({ + ...observabilityAIAssistantRouter, + push: (...args) => { + const next = link(...args); + + history.push(next); + }, + replace: (path, ...args) => { + const next = link(path, ...args); + history.replace(next); + }, + link: (path, ...args) => { + return http.basePath.prepend('/app/observabilityAIAssistant' + link(path, ...args)); + }, + }), + [http, history] + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts new file mode 100644 index 0000000000000..cb608450fd82e --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts @@ -0,0 +1,401 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { FindActionResult } from '@kbn/actions-plugin/server'; +import { AbortError } from '@kbn/kibana-utils-plugin/common'; +import { + act, + renderHook, + type Renderer, + type RenderHookResult, +} from '@testing-library/react-hooks'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { MessageRole } from '../../common'; +import type { PendingMessage } from '../types'; +import { useTimeline, UseTimelineResult } from './use_timeline'; + +type HookProps = Parameters[0]; + +const WAIT_OPTIONS = { timeout: 1500 }; + +describe('useTimeline', () => { + let hookResult: RenderHookResult>; + + describe('with an empty conversation', () => { + beforeAll(() => { + hookResult = renderHook((props) => useTimeline(props), { + initialProps: { + connectors: { + loading: false, + selectedConnector: 'OpenAI', + selectConnector: () => {}, + connectors: [{ id: 'OpenAI' }] as FindActionResult[], + }, + service: {}, + messages: [], + onChatComplete: jest.fn(), + onChatUpdate: jest.fn(), + } as unknown as HookProps, + }); + }); + it('renders the correct timeline items', () => { + expect(hookResult.result.current.items.length).toEqual(1); + + expect(hookResult.result.current.items[0]).toEqual({ + canEdit: false, + canRegenerate: false, + canGiveFeedback: false, + role: MessageRole.User, + title: 'started a conversation', + loading: false, + id: expect.any(String), + }); + }); + }); + + describe('with an existing conversation', () => { + beforeAll(() => { + hookResult = renderHook((props) => useTimeline(props), { + initialProps: { + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Hello', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: 'Goodbye', + }, + }, + ], + connectors: { + selectedConnector: 'foo', + }, + service: { + chat: () => {}, + }, + } as unknown as HookProps, + }); + }); + it('renders the correct timeline items', () => { + expect(hookResult.result.current.items.length).toEqual(3); + + expect(hookResult.result.current.items[1]).toEqual({ + canEdit: true, + canRegenerate: false, + canGiveFeedback: false, + role: MessageRole.User, + content: 'Hello', + loading: false, + id: expect.any(String), + title: '', + }); + + expect(hookResult.result.current.items[2]).toEqual({ + canEdit: false, + canRegenerate: true, + canGiveFeedback: true, + role: MessageRole.Assistant, + content: 'Goodbye', + loading: false, + id: expect.any(String), + title: '', + }); + }); + }); + + describe('when submitting a new prompt', () => { + let subject: Subject; + + let props: Omit & { + onChatUpdate: jest.MockedFn; + onChatComplete: jest.MockedFn; + service: Omit & { + executeFunction: jest.MockedFn; + }; + }; + + beforeEach(() => { + props = { + messages: [], + connectors: { + selectedConnector: 'foo', + }, + service: { + chat: jest.fn().mockImplementation(() => { + subject = new BehaviorSubject({ + message: { + role: MessageRole.Assistant, + content: '', + }, + }); + return subject; + }), + executeFunction: jest.fn(), + }, + onChatUpdate: jest.fn().mockImplementation((messages) => { + props = { ...props, messages }; + hookResult.rerender(props as unknown as HookProps); + }), + onChatComplete: jest.fn(), + } as any; + + hookResult = renderHook((nextProps) => useTimeline(nextProps), { + initialProps: props as unknown as HookProps, + }); + }); + + describe("and it's loading", () => { + beforeEach(() => { + act(() => { + hookResult.result.current.onSubmit({ + '@timestamp': new Date().toISOString(), + message: { role: MessageRole.User, content: 'Hello' }, + }); + }); + }); + + it('adds two items of which the last one is loading', async () => { + expect(hookResult.result.current.items[0].role).toEqual(MessageRole.User); + expect(hookResult.result.current.items[1].role).toEqual(MessageRole.User); + + expect(hookResult.result.current.items[2].role).toEqual(MessageRole.Assistant); + + expect(hookResult.result.current.items[1]).toMatchObject({ + role: MessageRole.User, + content: 'Hello', + loading: false, + }); + + expect(hookResult.result.current.items[2]).toMatchObject({ + role: MessageRole.Assistant, + content: '', + loading: true, + canRegenerate: false, + canGiveFeedback: false, + }); + + expect(hookResult.result.current.items.length).toBe(3); + + expect(hookResult.result.current.items[2]).toMatchObject({ + role: MessageRole.Assistant, + content: '', + loading: true, + canRegenerate: false, + canGiveFeedback: false, + }); + + act(() => { + subject.next({ message: { role: MessageRole.Assistant, content: 'Goodbye' } }); + }); + + expect(hookResult.result.current.items[2]).toMatchObject({ + role: MessageRole.Assistant, + content: 'Goodbye', + loading: true, + canRegenerate: false, + canGiveFeedback: false, + }); + + act(() => { + subject.complete(); + }); + + await hookResult.waitForNextUpdate(WAIT_OPTIONS); + + expect(hookResult.result.current.items[2]).toMatchObject({ + role: MessageRole.Assistant, + content: 'Goodbye', + loading: false, + canRegenerate: true, + canGiveFeedback: true, + }); + }); + + describe('and it is being aborted', () => { + beforeEach(() => { + act(() => { + subject.next({ message: { role: MessageRole.Assistant, content: 'My partial' } }); + subject.next({ + message: { + role: MessageRole.Assistant, + content: 'My partial', + }, + aborted: true, + error: new AbortError(), + }); + subject.complete(); + }); + }); + + it('adds the partial response', async () => { + expect(hookResult.result.current.items.length).toBe(3); + + expect(hookResult.result.current.items[2]).toEqual({ + canEdit: false, + canRegenerate: true, + canGiveFeedback: false, + content: 'My partial', + id: expect.any(String), + loading: false, + title: '', + role: MessageRole.Assistant, + error: expect.any(AbortError), + }); + }); + + describe('and it being regenerated', () => { + beforeEach(() => { + act(() => { + hookResult.result.current.onRegenerate(hookResult.result.current.items[2]); + subject.next({ message: { role: MessageRole.Assistant, content: '' } }); + }); + }); + + it('updates the last item in the array to be loading', () => { + expect(hookResult.result.current.items[2]).toEqual({ + canEdit: false, + canRegenerate: false, + canGiveFeedback: false, + content: '', + id: expect.any(String), + loading: true, + title: '', + role: MessageRole.Assistant, + }); + }); + + describe('and it is regenerated again', () => { + beforeEach(async () => { + act(() => { + hookResult.result.current.onStopGenerating(); + }); + + act(() => { + hookResult.result.current.onRegenerate(hookResult.result.current.items[2]); + }); + }); + + it('updates the last item to be not loading again', async () => { + expect(hookResult.result.current.items.length).toBe(3); + + expect(hookResult.result.current.items[2]).toEqual({ + canEdit: false, + canRegenerate: false, + canGiveFeedback: false, + content: '', + id: expect.any(String), + loading: true, + title: '', + role: MessageRole.Assistant, + }); + + act(() => { + subject.next({ message: { role: MessageRole.Assistant, content: 'Regenerated' } }); + subject.complete(); + }); + + await hookResult.waitForNextUpdate(WAIT_OPTIONS); + + expect(hookResult.result.current.items.length).toBe(3); + + expect(hookResult.result.current.items[2]).toEqual({ + canEdit: false, + canRegenerate: true, + canGiveFeedback: true, + content: 'Regenerated', + id: expect.any(String), + loading: false, + title: '', + role: MessageRole.Assistant, + }); + }); + }); + }); + }); + + describe('and a function call is returned', () => { + it('the function call is executed and its response is sent as a user reply', async () => { + jest.clearAllMocks(); + + act(() => { + subject.next({ + message: { + role: MessageRole.Assistant, + function_call: { + trigger: MessageRole.Assistant, + name: 'my_function', + arguments: '{}', + }, + }, + }); + subject.complete(); + }); + + props.service.executeFunction.mockResolvedValueOnce({ + content: { + message: 'my-response', + }, + }); + + await hookResult.waitForNextUpdate(WAIT_OPTIONS); + + expect(props.onChatUpdate).toHaveBeenCalledTimes(2); + + expect( + props.onChatUpdate.mock.calls[0][0].map( + (msg) => msg.message.content || msg.message.function_call?.name + ) + ).toEqual(['Hello', 'my_function']); + + expect( + props.onChatUpdate.mock.calls[1][0].map( + (msg) => msg.message.content || msg.message.function_call?.name + ) + ).toEqual(['Hello', 'my_function', JSON.stringify({ message: 'my-response' })]); + + expect(props.onChatComplete).not.toHaveBeenCalled(); + + expect(props.service.executeFunction).toHaveBeenCalledWith( + 'my_function', + '{}', + expect.any(Object) + ); + + act(() => { + subject.next({ + message: { + role: MessageRole.Assistant, + content: 'looks like my-function returned my-response', + }, + }); + subject.complete(); + }); + + await hookResult.waitForNextUpdate(WAIT_OPTIONS); + + expect(props.onChatComplete).toHaveBeenCalledTimes(1); + + expect( + props.onChatComplete.mock.calls[0][0].map( + (msg) => msg.message.content || msg.message.function_call?.name + ) + ).toEqual([ + 'Hello', + 'my_function', + JSON.stringify({ message: 'my-response' }), + 'looks like my-function returned my-response', + ]); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts new file mode 100644 index 0000000000000..ffe247ef5d6f9 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts @@ -0,0 +1,215 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { AbortError } from '@kbn/kibana-utils-plugin/common'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import type { Subscription } from 'rxjs'; +import { MessageRole, type ConversationCreateRequest, type Message } from '../../common/types'; +import type { ChatPromptEditorProps } from '../components/chat/chat_prompt_editor'; +import type { ChatTimelineProps } from '../components/chat/chat_timeline'; +import { EMPTY_CONVERSATION_TITLE } from '../i18n'; +import { getSystemMessage } from '../service/get_system_message'; +import type { ObservabilityAIAssistantService, PendingMessage } from '../types'; +import { getTimelineItemsfromConversation } from '../utils/get_timeline_items_from_conversation'; +import type { UseGenAIConnectorsResult } from './use_genai_connectors'; + +export function createNewConversation(): ConversationCreateRequest { + return { + '@timestamp': new Date().toISOString(), + messages: [getSystemMessage()], + conversation: { + title: EMPTY_CONVERSATION_TITLE, + }, + labels: {}, + numeric_labels: {}, + public: false, + }; +} + +export type UseTimelineResult = Pick< + ChatTimelineProps, + 'onEdit' | 'onFeedback' | 'onRegenerate' | 'onStopGenerating' | 'items' +> & + Pick; + +export function useTimeline({ + messages, + connectors, + currentUser, + service, + onChatUpdate, + onChatComplete, +}: { + messages: Message[]; + connectors: UseGenAIConnectorsResult; + currentUser?: Pick; + service: ObservabilityAIAssistantService; + onChatUpdate: (messages: Message[]) => void; + onChatComplete: (messages: Message[]) => void; +}): UseTimelineResult { + const connectorId = connectors.selectedConnector; + + const hasConnector = !!connectorId; + + const conversationItems = useMemo(() => { + return getTimelineItemsfromConversation({ + messages, + currentUser, + hasConnector, + }); + }, [messages, currentUser, hasConnector]); + + const [subscription, setSubscription] = useState(); + + const controllerRef = useRef(new AbortController()); + + const [pendingMessage, setPendingMessage] = useState(); + + function chat(nextMessages: Message[]): Promise { + const controller = new AbortController(); + + return new Promise((resolve, reject) => { + if (!connectorId) { + reject(new Error('Can not add a message without a connector')); + return; + } + + onChatUpdate(nextMessages); + + const response$ = service.chat({ messages: nextMessages, connectorId }); + + let pendingMessageLocal = pendingMessage; + + const nextSubscription = response$.subscribe({ + next: (nextPendingMessage) => { + pendingMessageLocal = nextPendingMessage; + setPendingMessage(() => nextPendingMessage); + }, + error: reject, + complete: () => { + resolve(pendingMessageLocal!); + }, + }); + + setSubscription(() => { + controllerRef.current = controller; + return nextSubscription; + }); + }).then(async (reply) => { + if (reply.error) { + return nextMessages; + } + if (reply.aborted) { + return nextMessages; + } + + setPendingMessage(undefined); + + const messagesAfterChat = nextMessages.concat({ + '@timestamp': new Date().toISOString(), + message: { + ...reply.message, + }, + }); + + onChatUpdate(messagesAfterChat); + + if (reply?.message.function_call?.name) { + const name = reply.message.function_call.name; + + try { + const message = await service.executeFunction( + name, + reply.message.function_call.arguments, + controller.signal + ); + + return await chat( + messagesAfterChat.concat({ + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + name, + content: JSON.stringify(message.content), + data: JSON.stringify(message.data), + }, + }) + ); + } catch (error) { + return await chat( + messagesAfterChat.concat({ + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + name, + content: JSON.stringify({ + message: error.toString(), + ...error.body, + }), + }, + }) + ); + } + } + + return messagesAfterChat; + }); + } + + const items = useMemo(() => { + if (pendingMessage) { + return conversationItems.concat({ + id: '', + canEdit: false, + canRegenerate: pendingMessage.aborted || !!pendingMessage.error, + canGiveFeedback: false, + title: '', + role: pendingMessage.message.role, + content: pendingMessage.message.content, + loading: !pendingMessage.aborted && !pendingMessage.error, + function_call: pendingMessage.message.function_call, + currentUser, + error: pendingMessage.error, + }); + } + + return conversationItems; + }, [conversationItems, pendingMessage, currentUser]); + + useEffect(() => { + return () => { + subscription?.unsubscribe(); + }; + }, [subscription]); + + return { + items, + onEdit: (item, content) => {}, + onFeedback: (item, feedback) => {}, + onRegenerate: (item) => { + const indexOf = items.indexOf(item); + chat(messages.slice(0, indexOf - 1)).then((nextMessages) => onChatComplete(nextMessages)); + }, + onStopGenerating: () => { + subscription?.unsubscribe(); + setPendingMessage((prevPendingMessage) => ({ + message: { + role: MessageRole.Assistant, + ...prevPendingMessage?.message, + }, + aborted: true, + error: new AbortError(), + })); + setSubscription(undefined); + }, + onSubmit: async (message) => { + const nextMessages = await chat(messages.concat(message)); + onChatComplete(nextMessages); + }, + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/i18n.ts b/x-pack/plugins/observability_ai_assistant/public/i18n.ts new file mode 100644 index 0000000000000..393d4ae7e8f10 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/i18n.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const EMPTY_CONVERSATION_TITLE = i18n.translate( + 'xpack.observabilityAiAssistant.emptyConversationTitle', + { defaultMessage: 'New conversation' } +); diff --git a/x-pack/plugins/observability_ai_assistant/public/plugin.ts b/x-pack/plugins/observability_ai_assistant/public/plugin.ts deleted file mode 100644 index 187275b127dd3..0000000000000 --- a/x-pack/plugins/observability_ai_assistant/public/plugin.ts +++ /dev/null @@ -1,48 +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 type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; -import type { Logger } from '@kbn/logging'; -import { createService } from './service/create_service'; -import type { - ConfigSchema, - ObservabilityAIAssistantPluginSetup, - ObservabilityAIAssistantPluginSetupDependencies, - ObservabilityAIAssistantPluginStart, - ObservabilityAIAssistantPluginStartDependencies, -} from './types'; - -export class ObservabilityAIAssistantPlugin - implements - Plugin< - ObservabilityAIAssistantPluginSetup, - ObservabilityAIAssistantPluginStart, - ObservabilityAIAssistantPluginSetupDependencies, - ObservabilityAIAssistantPluginStartDependencies - > -{ - logger: Logger; - constructor(context: PluginInitializerContext) { - this.logger = context.logger.get(); - } - setup( - core: CoreSetup, - pluginsSetup: ObservabilityAIAssistantPluginSetupDependencies - ): ObservabilityAIAssistantPluginSetup { - return {}; - } - - start( - coreStart: CoreStart, - pluginsStart: ObservabilityAIAssistantPluginStartDependencies - ): ObservabilityAIAssistantPluginStart { - return createService({ - coreStart, - enabled: coreStart.application.capabilities.observabilityAIAssistant.show === true, - }); - } -} diff --git a/x-pack/plugins/observability_ai_assistant/public/plugin.tsx b/x-pack/plugins/observability_ai_assistant/public/plugin.tsx new file mode 100644 index 0000000000000..6ceff112ada4c --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/plugin.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 { + AppNavLinkStatus, + DEFAULT_APP_CATEGORIES, + type AppMountParameters, + type CoreSetup, + type CoreStart, + type Plugin, + type PluginInitializerContext, +} from '@kbn/core/public'; +import { i18n } from '@kbn/i18n'; +import type { Logger } from '@kbn/logging'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import type { + ContextRegistry, + FunctionRegistry, + RegisterContextDefinition, + RegisterFunctionDefinition, +} from '../common/types'; +import { registerFunctions } from './functions'; +import { createService } from './service/create_service'; +import type { + ConfigSchema, + ObservabilityAIAssistantPluginSetup, + ObservabilityAIAssistantPluginSetupDependencies, + ObservabilityAIAssistantPluginStart, + ObservabilityAIAssistantPluginStartDependencies, + ObservabilityAIAssistantService, +} from './types'; + +export class ObservabilityAIAssistantPlugin + implements + Plugin< + ObservabilityAIAssistantPluginSetup, + ObservabilityAIAssistantPluginStart, + ObservabilityAIAssistantPluginSetupDependencies, + ObservabilityAIAssistantPluginStartDependencies + > +{ + logger: Logger; + service?: ObservabilityAIAssistantService; + + constructor(context: PluginInitializerContext) { + this.logger = context.logger.get(); + } + setup( + coreSetup: CoreSetup, + pluginsSetup: ObservabilityAIAssistantPluginSetupDependencies + ): ObservabilityAIAssistantPluginSetup { + coreSetup.application.register({ + id: 'observabilityAIAssistant', + title: i18n.translate('xpack.observabilityAiAssistant.appTitle', { + defaultMessage: 'Observability AI Assistant', + }), + euiIconType: 'logoObservability', + appRoute: '/app/observabilityAIAssistant', + category: DEFAULT_APP_CATEGORIES.observability, + navLinkStatus: AppNavLinkStatus.hidden, + deepLinks: [ + { + id: 'conversations', + title: i18n.translate('xpack.observabilityAiAssistant.conversationsDeepLinkTitle', { + defaultMessage: 'Conversations', + }), + path: '/conversations', + }, + ], + + mount: async (appMountParameters: AppMountParameters) => { + // Load application bundle and Get start services + const [{ Application }, [coreStart, pluginsStart]] = await Promise.all([ + import('./application'), + coreSetup.getStartServices(), + ]); + + ReactDOM.render( + , + appMountParameters.element + ); + + return () => { + ReactDOM.unmountComponentAtNode(appMountParameters.element); + }; + }, + }); + return {}; + } + + start( + coreStart: CoreStart, + pluginsStart: ObservabilityAIAssistantPluginStartDependencies + ): ObservabilityAIAssistantPluginStart { + const contextRegistry: ContextRegistry = new Map(); + const functionRegistry: FunctionRegistry = new Map(); + + const service = (this.service = createService({ + coreStart, + securityStart: pluginsStart.security, + contextRegistry, + functionRegistry, + enabled: coreStart.application.capabilities.observabilityAIAssistant.show === true, + })); + + const registerContext: RegisterContextDefinition = (context) => { + contextRegistry.set(context.name, context); + }; + + const registerFunction: RegisterFunctionDefinition = (def, respond, render) => { + functionRegistry.set(def.name, { options: def, respond, render }); + }; + + registerFunctions({ + registerContext, + registerFunction, + service, + }); + + return { + ...service, + registerContext, + registerFunction, + }; + } +} diff --git a/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx b/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx new file mode 100644 index 0000000000000..db3384d7005d5 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx @@ -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 { createRouter, Outlet } from '@kbn/typed-react-router-config'; +import * as t from 'io-ts'; +import React from 'react'; +import { Redirect } from 'react-router-dom'; +import { ObservabilityAIAssistantPageTemplate } from '../components/page_template'; +import { ConversationView } from './conversations/conversation_view'; + +/** + * The array of route definitions to be used when the application + * creates the routes. + */ +const observabilityAIAssistantRoutes = { + '/': { + element: , + }, + '/conversations': { + element: ( + + + + ), + children: { + '/conversations/new': { + element: , + }, + '/conversations/{conversationId}': { + params: t.type({ + path: t.type({ + conversationId: t.string, + }), + }), + element: , + }, + }, + }, +}; + +export type ObservabilityAIAssistantRoutes = typeof observabilityAIAssistantRoutes; + +export const observabilityAIAssistantRouter = createRouter(observabilityAIAssistantRoutes); + +export type ObservabilityAIAssistantRouter = typeof observabilityAIAssistantRouter; diff --git a/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx b/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx new file mode 100644 index 0000000000000..4ffaa69d2acf8 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; +import { css } from '@emotion/css'; +import { i18n } from '@kbn/i18n'; +import { merge, omit } from 'lodash'; +import React, { useMemo, useState } from 'react'; +import type { ConversationCreateRequest, Message } from '../../../common/types'; +import { ChatBody } from '../../components/chat/chat_body'; +import { ConversationList } from '../../components/chat/conversation_list'; +import { AbortableAsyncState, useAbortableAsync } from '../../hooks/use_abortable_async'; +import { useConfirmModal } from '../../hooks/use_confirm_modal'; +import { useCurrentUser } from '../../hooks/use_current_user'; +import { useGenAIConnectors } from '../../hooks/use_genai_connectors'; +import { useKibana } from '../../hooks/use_kibana'; +import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant'; +import { useObservabilityAIAssistantParams } from '../../hooks/use_observability_ai_assistant_params'; +import { useObservabilityAIAssistantRouter } from '../../hooks/use_observability_ai_assistant_router'; +import { createNewConversation } from '../../hooks/use_timeline'; +import { EMPTY_CONVERSATION_TITLE } from '../../i18n'; +import { getConnectorsManagementHref } from '../../utils/get_connectors_management_href'; + +const containerClassName = css` + max-width: 100%; +`; + +const chatBodyContainerClassNameWithError = css` + align-self: center; +`; + +export function ConversationView() { + const connectors = useGenAIConnectors(); + + const currentUser = useCurrentUser(); + + const service = useObservabilityAIAssistant(); + + const observabilityAIAssistantRouter = useObservabilityAIAssistantRouter(); + + const { path } = useObservabilityAIAssistantParams('/conversations/*'); + + const { + services: { http, notifications }, + } = useKibana(); + + const { element: confirmDeleteElement, confirm: confirmDeleteFunction } = useConfirmModal({ + title: i18n.translate('xpack.observabilityAiAssistant.confirmDeleteConversationTitle', { + defaultMessage: 'Delete this conversation?', + }), + children: i18n.translate('xpack.observabilityAiAssistant.confirmDeleteConversationContent', { + defaultMessage: 'This action cannot be undone.', + }), + confirmButtonText: i18n.translate('xpack.observabilityAiAssistant.confirmDeleteButtonText', { + defaultMessage: 'Delete conversation', + }), + }); + + const [isUpdatingList, setIsUpdatingList] = useState(false); + + const conversationId = 'conversationId' in path ? path.conversationId : undefined; + + const conversation: AbortableAsyncState = + useAbortableAsync( + ({ signal }) => { + if (!conversationId) { + const nextConversation = createNewConversation(); + setDisplayedMessages(nextConversation.messages); + return nextConversation; + } + + return service + .callApi('GET /internal/observability_ai_assistant/conversation/{conversationId}', { + signal, + params: { path: { conversationId } }, + }) + .then((nextConversation) => { + setDisplayedMessages(nextConversation.messages); + return nextConversation; + }) + .catch((error) => { + setDisplayedMessages([]); + throw error; + }); + }, + [conversationId] + ); + + const conversations = useAbortableAsync( + ({ signal }) => { + return service.callApi('POST /internal/observability_ai_assistant/conversations', { + signal, + }); + }, + [service] + ); + + const displayedConversations = useMemo(() => { + return [ + ...(!conversationId ? [{ id: '', label: EMPTY_CONVERSATION_TITLE }] : []), + ...(conversations.value?.conversations ?? []).map((conv) => ({ + id: conv.conversation.id, + label: conv.conversation.title, + href: observabilityAIAssistantRouter.link('/conversations/{conversationId}', { + path: { + conversationId: conv.conversation.id, + }, + }), + })), + ]; + }, [conversations.value?.conversations, conversationId, observabilityAIAssistantRouter]); + + const [displayedMessages, setDisplayedMessages] = useState([]); + + function navigateToConversation(nextConversationId?: string) { + observabilityAIAssistantRouter.push( + nextConversationId ? '/conversations/{conversationId}' : '/conversations/new', + { + path: { conversationId: nextConversationId }, + query: {}, + } + ); + } + + return ( + <> + {confirmDeleteElement} + + + { + observabilityAIAssistantRouter.push('/conversations/{conversationId}', { + path: { + conversationId: nextConversationId, + }, + query: {}, + }); + }} + onClickNewChat={() => { + observabilityAIAssistantRouter.push('/conversations/new', { + path: {}, + query: {}, + }); + }} + onClickDeleteConversation={(id) => { + confirmDeleteFunction() + .then(async (confirmed) => { + if (!confirmed) { + return; + } + + setIsUpdatingList(true); + + await service.callApi( + 'DELETE /internal/observability_ai_assistant/conversation/{conversationId}', + { + params: { + path: { + conversationId: id, + }, + }, + signal: null, + } + ); + + const isCurrentConversation = id === conversationId; + const hasOtherConversations = conversations.value?.conversations.find( + (conv) => 'id' in conv.conversation && conv.conversation.id !== id + ); + + if (isCurrentConversation) { + navigateToConversation( + hasOtherConversations + ? conversations.value!.conversations[0].conversation.id + : undefined + ); + } + + conversations.refresh(); + }) + .catch((error) => { + notifications.toasts.addError(error, { + title: i18n.translate( + 'xpack.observabilityAiAssistant.failedToDeleteConversation', + { + defaultMessage: 'Could not delete conversation', + } + ), + }); + }) + .finally(() => { + setIsUpdatingList(false); + }); + }} + /> + + + {conversation.error ? ( + + {i18n.translate('xpack.observabilityAiAssistant.couldNotFindConversationContent', { + defaultMessage: + 'Could not find a conversation with id {conversationId}. Make sure the conversation exists and you have access to it.', + values: { conversationId }, + })} + + ) : null} + {conversation.loading ? : null} + {!conversation.error && conversation.value ? ( + { + const conversationObject = conversation.value!; + if (conversationId) { + service + .callApi( + `POST /internal/observability_ai_assistant/conversation/{conversationId}`, + { + signal: null, + params: { + path: { + conversationId, + }, + body: { + conversation: merge( + { + '@timestamp': conversationObject['@timestamp'], + conversation: { + id: conversationId, + }, + }, + omit( + conversationObject, + 'conversation.last_updated', + 'namespace', + 'user' + ), + { messages } + ), + }, + }, + } + ) + .then(() => { + conversations.refresh(); + }) + .catch((err) => { + notifications.toasts.addError(err, { + title: i18n.translate( + 'xpack.observabilityAiAssistant.errorCreatingConversation', + { defaultMessage: 'Could not create conversation' } + ), + }); + }); + } else { + service + .callApi(`PUT /internal/observability_ai_assistant/conversation`, { + signal: null, + params: { + body: { + conversation: merge({}, conversationObject, { messages }), + }, + }, + }) + .then((createdConversation) => { + navigateToConversation(createdConversation.conversation.id); + conversations.refresh(); + }) + .catch((err) => { + notifications.toasts.addError(err, { + title: i18n.translate( + 'xpack.observabilityAiAssistant.errorCreatingConversation', + { defaultMessage: 'Could not create conversation' } + ), + }); + }); + } + }} + onChatUpdate={(messages) => { + setDisplayedMessages(messages); + }} + /> + ) : null} + + + + + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/service/create_service.test.ts b/x-pack/plugins/observability_ai_assistant/public/service/create_service.test.ts index 8a7ec44bfd9bc..4e9eb3d61d13b 100644 --- a/x-pack/plugins/observability_ai_assistant/public/service/create_service.test.ts +++ b/x-pack/plugins/observability_ai_assistant/public/service/create_service.test.ts @@ -4,10 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { CoreStart } from '@kbn/core/public'; +import type { CoreStart, HttpFetchOptions } from '@kbn/core/public'; import { ReadableStream } from 'stream/web'; -import { ObservabilityAIAssistantService } from '../types'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import type { ObservabilityAIAssistantService } from '../types'; import { createService } from './create_service'; +import { SecurityPluginStart } from '@kbn/security-plugin/public'; +import { lastValueFrom } from 'rxjs'; describe('createService', () => { describe('chat', () => { @@ -15,14 +18,14 @@ describe('createService', () => { const httpPostSpy = jest.fn(); - function respondWithChunks({ chunks, status = 200 }: { status?: number; chunks: string[][] }) { + function respondWithChunks({ chunks, status = 200 }: { status?: number; chunks: string[] }) { const response = { response: { status, body: new ReadableStream({ start(controller) { chunks.forEach((chunk) => { - controller.enqueue(new TextEncoder().encode(chunk.join('\n'))); + controller.enqueue(new TextEncoder().encode(chunk)); }); controller.close(); }, @@ -33,10 +36,8 @@ describe('createService', () => { httpPostSpy.mockResolvedValueOnce(response); } - async function chat(signal: AbortSignal = new AbortController().signal) { - const response = await service.chat({ messages: [], connectorId: '', signal }); - - return response; + function chat() { + return service.chat({ messages: [], connectorId: '' }); } beforeEach(() => { @@ -46,6 +47,13 @@ describe('createService', () => { post: httpPostSpy, }, } as unknown as CoreStart, + securityStart: { + authc: { + getCurrentUser: () => Promise.resolve({ username: 'elastic' } as AuthenticatedUser), + }, + } as unknown as SecurityPluginStart, + contextRegistry: new Map(), + functionRegistry: new Map(), enabled: true, }); }); @@ -55,56 +63,144 @@ describe('createService', () => { }); it('correctly parses a stream of JSON lines', async () => { - const chunk1 = ['data: {}', 'data: {}']; - const chunk2 = ['data: {}', 'data: [DONE]']; + const chunk1 = + 'data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"}}]}'; + const chunk2 = + '\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" message"}}]}\ndata: [DONE]'; respondWithChunks({ chunks: [chunk1, chunk2] }); - const response$ = await chat(); + const response$ = chat(); const results: any = []; - response$.subscribe({ + + const subscription = response$.subscribe({ next: (data) => results.push(data), complete: () => { - expect(results).toHaveLength(3); + expect(results).toHaveLength(4); + }, + }); + + const value = await lastValueFrom(response$); + subscription.unsubscribe(); + + expect(value).toEqual({ + message: { + role: 'assistant', + content: 'My new message', + function_call: { + arguments: '', + name: '', + trigger: 'assistant', + }, }, }); }); it('correctly buffers partial lines', async () => { - const chunk1 = ['data: {}', 'data: {']; - const chunk2 = ['}', 'data: [DONE]']; + const chunk1 = + 'data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"'; + const chunk2 = + '}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" message"}}]}\ndata: [DONE]'; respondWithChunks({ chunks: [chunk1, chunk2] }); - const response$ = await chat(); + const response$ = chat(); const results: any = []; - response$.subscribe({ - next: (data) => results.push(data), - complete: () => { - expect(results).toHaveLength(2); + + await new Promise((resolve, reject) => { + response$.subscribe({ + next: (data) => { + results.push(data); + }, + error: reject, + complete: resolve, + }); + }); + + const value = await lastValueFrom(response$); + + expect(results).toHaveLength(4); + + expect(value).toEqual({ + message: { + role: 'assistant', + content: 'My new message', + function_call: { + arguments: '', + name: '', + trigger: 'assistant', + }, }, }); }); - it('propagates invalid requests as an error', () => { + it('catches invalid requests and flags it as an error', async () => { respondWithChunks({ status: 400, chunks: [] }); - expect(() => chat()).rejects.toThrowErrorMatchingInlineSnapshot(`"Unexpected error"`); + const response$ = chat(); + + const value = await lastValueFrom(response$); + + expect(value).toEqual({ + aborted: false, + error: expect.any(Error), + message: { + role: 'assistant', + }, + }); }); it('propagates JSON parsing errors', async () => { - const chunk1 = ['data: {}', 'data: invalid json']; + respondWithChunks({ chunks: ['data: {}', 'data: invalid json'] }); + + const response$ = chat(); + + const value = await lastValueFrom(response$); + + expect(value).toEqual({ + aborted: false, + error: expect.any(Error), + message: { + role: 'assistant', + }, + }); + }); + + it('cancels a running http request when aborted', async () => { + httpPostSpy.mockImplementationOnce((endpoint: string, options: HttpFetchOptions) => { + options.signal?.addEventListener('abort', () => { + expect(options.signal?.aborted).toBeTruthy(); + }); + return Promise.resolve({ + response: { + status: 200, + body: new ReadableStream({ + start(controller) {}, + }), + }, + }); + }); + + const response$ = chat(); - respondWithChunks({ chunks: [chunk1] }); + await new Promise((resolve, reject) => { + const subscription = response$.subscribe({}); + + setTimeout(() => { + subscription.unsubscribe(); + resolve(); + }, 100); + }); - const response$ = await chat(); + const value = await lastValueFrom(response$); - response$.subscribe({ - error: (err) => { - expect(err).toBeInstanceOf(SyntaxError); + expect(value).toEqual({ + message: { + role: 'assistant', }, + aborted: true, }); }); }); diff --git a/x-pack/plugins/observability_ai_assistant/public/service/create_service.ts b/x-pack/plugins/observability_ai_assistant/public/service/create_service.ts index da29de585c9d1..c977b8c3137c6 100644 --- a/x-pack/plugins/observability_ai_assistant/public/service/create_service.ts +++ b/x-pack/plugins/observability_ai_assistant/public/service/create_service.ts @@ -6,65 +6,199 @@ */ import type { CoreStart, HttpResponse } from '@kbn/core/public'; -import { filter, map } from 'rxjs'; +import { AbortError } from '@kbn/kibana-utils-plugin/common'; +import { SecurityPluginStart } from '@kbn/security-plugin/public'; +import { IncomingMessage } from 'http'; +import { cloneDeep } from 'lodash'; +import { + BehaviorSubject, + catchError, + concatMap, + delay, + filter as rxJsFilter, + finalize, + map, + of, + scan, + shareReplay, +} from 'rxjs'; import type { Message } from '../../common'; +import { ContextRegistry, FunctionRegistry, MessageRole } from '../../common/types'; import { createCallObservabilityAIAssistantAPI } from '../api'; -import { CreateChatCompletionResponseChunk, ObservabilityAIAssistantService } from '../types'; +import type { + CreateChatCompletionResponseChunk, + ObservabilityAIAssistantService, + PendingMessage, +} from '../types'; import { readableStreamReaderIntoObservable } from '../utils/readable_stream_reader_into_observable'; export function createService({ coreStart, + securityStart, + functionRegistry, + contextRegistry, enabled, }: { coreStart: CoreStart; + securityStart: SecurityPluginStart; + functionRegistry: FunctionRegistry; + contextRegistry: ContextRegistry; enabled: boolean; }): ObservabilityAIAssistantService { const client = createCallObservabilityAIAssistantAPI(coreStart); + const getContexts: ObservabilityAIAssistantService['getContexts'] = () => { + return Array.from(contextRegistry.values()); + }; + const getFunctions: ObservabilityAIAssistantService['getFunctions'] = ({ + contexts, + filter, + } = {}) => { + const allFunctions = Array.from(functionRegistry.values()); + + return contexts || filter + ? allFunctions.filter((fn) => { + const matchesContext = + !contexts || fn.options.contexts.some((context) => contexts.includes(context)); + const matchesFilter = + !filter || fn.options.name.includes(filter) || fn.options.description.includes(filter); + + return matchesContext && matchesFilter; + }) + : allFunctions; + }; + return { isEnabled: () => { return enabled; }, - async chat({ - connectorId, - messages, - signal, - }: { - connectorId: string; - messages: Message[]; - signal: AbortSignal; - }) { - const response = (await client('POST /internal/observability_ai_assistant/chat', { + chat({ connectorId, messages }: { connectorId: string; messages: Message[] }) { + const subject = new BehaviorSubject({ + message: { + role: MessageRole.Assistant, + }, + }); + + const contexts = ['core']; + + const functions = getFunctions({ contexts }); + + const controller = new AbortController(); + + client('POST /internal/observability_ai_assistant/chat', { params: { body: { messages, connectorId, + functions: functions.map((fn) => fn.options), }, }, - signal, + signal: controller.signal, asResponse: true, rawResponse: true, - })) as unknown as HttpResponse; + }) + .then((_response) => { + const response = _response as unknown as HttpResponse; - const status = response.response?.status; + const status = response.response?.status; - if (!status || status >= 400) { - throw new Error(response.response?.statusText || 'Unexpected error'); - } + if (!status || status >= 400) { + throw new Error(response.response?.statusText || 'Unexpected error'); + } - const reader = response.response.body?.getReader(); + const reader = response.response.body?.getReader(); - if (!reader) { - throw new Error('Could not get reader from response'); - } + if (!reader) { + throw new Error('Could not get reader from response'); + } - return readableStreamReaderIntoObservable(reader).pipe( - map((line) => line.substring(6)), - filter((line) => !!line && line !== '[DONE]'), - map((line) => JSON.parse(line) as CreateChatCompletionResponseChunk), - filter((line) => line.object === 'chat.completion.chunk') + const subscription = readableStreamReaderIntoObservable(reader) + .pipe( + map((line) => line.substring(6)), + rxJsFilter((line) => !!line && line !== '[DONE]'), + map((line) => JSON.parse(line) as CreateChatCompletionResponseChunk), + rxJsFilter((line) => line.object === 'chat.completion.chunk'), + scan( + (acc, { choices }) => { + acc.message.content += choices[0].delta.content ?? ''; + acc.message.function_call.name += choices[0].delta.function_call?.name ?? ''; + acc.message.function_call.arguments += + choices[0].delta.function_call?.arguments ?? ''; + return cloneDeep(acc); + }, + { + message: { + content: '', + function_call: { + name: '', + arguments: '', + trigger: MessageRole.Assistant as const, + }, + role: MessageRole.Assistant, + }, + } + ), + catchError((error) => + of({ + ...subject.value, + error, + aborted: error instanceof AbortError || controller.signal.aborted, + }) + ) + ) + .subscribe(subject); + + controller.signal.addEventListener('abort', () => { + subscription.unsubscribe(); + subject.next({ + ...subject.value, + aborted: true, + }); + subject.complete(); + }); + }) + .catch((err) => { + subject.next({ + ...subject.value, + aborted: false, + error: err, + }); + subject.complete(); + }); + + return subject.pipe( + concatMap((value) => of(value).pipe(delay(50))), + shareReplay(1), + finalize(() => { + controller.abort(); + }) ); }, callApi: client, + getCurrentUser: () => securityStart.authc.getCurrentUser(), + getContexts, + getFunctions, + executeFunction: async (name, args, signal) => { + const fn = functionRegistry.get(name); + + if (!fn) { + throw new Error(`Function ${name} not found`); + } + + const parsedArguments = args ? JSON.parse(args) : {}; + + // validate + + return await fn.respond({ arguments: parsedArguments }, signal); + }, + renderFunction: (name, response) => { + const fn = functionRegistry.get(name); + + if (!fn) { + throw new Error(`Function ${name} not found`); + } + + return fn.render?.({ response }); + }, }; } diff --git a/x-pack/plugins/observability_ai_assistant/public/service/get_system_message.ts b/x-pack/plugins/observability_ai_assistant/public/service/get_system_message.ts new file mode 100644 index 0000000000000..5193d98d9f6b6 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/service/get_system_message.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 dedent from 'dedent'; +import { MessageRole } from '../../common'; + +export function getSystemMessage() { + return { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System as const, + content: + dedent(`You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities. + + You can use the "summarise" functions to store new information you have learned in a knowledge database. Once you have established that you did not know the answer to a question, and the user gave you this information, it's important that you create a summarisation of what you have learned and store it in the knowledge database. When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic search later. + + Additionally, you can use the "recall" function to retrieve relevant information from the knowledge database. Using the "recall" function will allow you to create a much more tailored and improved user experience. Assume the user has told you before. + + For every question that the user asks, you, as the assistant, must query the knowledge base, no matter how confident you are in your response. + + When the user starts a conversation, you as the assistant, MUST ALWAYS use the "recall" function to make sure you have enough context from the knowledge database to understand their question. DO NOT assume your knowledge is sufficient to answer the question appropriately. Make sure you mention this to the user in your first reply to them. + + You do have access to private data that the user has shared with you in previous conversations. + + Given this ability to store and recall data, it is very important to not make assumptions about the user's data or preferences, but rather, first query the knowledge database, and if nothing relevant comes up, ask the user for clarification. This is very important! They could be storing their data in any field, and in any data stream or index. + + RIGHT: + User: "What is NASA" + Assistant executes recall function + Assistant answers question with data from recall function response + + WRONG: + User: "What is NASA" + Assistant answers question without querying the knowledge + + You should autonomously execute these functions - do not wait on the user's permission, but be proactive. + + Note that any visualisations will be displayed ABOVE your textual response, not below. + + Feel free to use Markdown in your replies, especially for code and query statements.`), + }, + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/types.ts b/x-pack/plugins/observability_ai_assistant/public/types.ts index 69d826c7ebf17..02e626185fdb7 100644 --- a/x-pack/plugins/observability_ai_assistant/public/types.ts +++ b/x-pack/plugins/observability_ai_assistant/public/types.ts @@ -4,18 +4,33 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { FeaturesPluginStart, FeaturesPluginSetup } from '@kbn/features-plugin/public'; +import type { + ObservabilitySharedPluginSetup, + ObservabilitySharedPluginStart, +} from '@kbn/observability-shared-plugin/public'; +import type { + AuthenticatedUser, + SecurityPluginSetup, + SecurityPluginStart, +} from '@kbn/security-plugin/public'; import type { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; -import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; +import type { Serializable } from '@kbn/utility-types'; import type { CreateChatCompletionResponse, CreateChatCompletionResponseChoicesInner, } from 'openai'; import type { Observable } from 'rxjs'; -import type { FeaturesPluginSetup, FeaturesPluginStart } from '@kbn/features-plugin/public'; -import type { Message } from '../common/types'; +import type { + ContextDefinition, + FunctionDefinition, + Message, + RegisterContextDefinition, + RegisterFunctionDefinition, +} from '../common/types'; import type { ObservabilityAIAssistantAPIClient } from './api'; /* eslint-disable @typescript-eslint/no-empty-interface*/ @@ -23,32 +38,51 @@ import type { ObservabilityAIAssistantAPIClient } from './api'; export type CreateChatCompletionResponseChunk = Omit & { choices: Array< Omit & { - delta: { content?: string; function_call?: { name?: string; args?: string } }; + delta: { content?: string; function_call?: { name?: string; arguments?: string } }; } >; }; +export interface PendingMessage { + message: Message['message']; + aborted?: boolean; + error?: any; +} + export interface ObservabilityAIAssistantService { isEnabled: () => boolean; - chat: (options: { - messages: Message[]; - connectorId: string; - signal: AbortSignal; - }) => Promise>; + chat: (options: { messages: Message[]; connectorId: string }) => Observable; callApi: ObservabilityAIAssistantAPIClient; + getCurrentUser: () => Promise; + getContexts: () => ContextDefinition[]; + getFunctions: (options?: { contexts?: string[]; filter?: string }) => FunctionDefinition[]; + executeFunction: ( + name: string, + args: string | undefined, + signal: AbortSignal + ) => Promise<{ content?: Serializable; data?: Serializable }>; + renderFunction: ( + name: string, + response: { data?: Serializable; content?: Serializable } + ) => React.ReactNode; } -export interface ObservabilityAIAssistantPluginStart extends ObservabilityAIAssistantService {} +export interface ObservabilityAIAssistantPluginStart extends ObservabilityAIAssistantService { + registerContext: RegisterContextDefinition; + registerFunction: RegisterFunctionDefinition; +} export interface ObservabilityAIAssistantPluginSetup {} export interface ObservabilityAIAssistantPluginSetupDependencies { triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; security: SecurityPluginSetup; features: FeaturesPluginSetup; + observabilityShared: ObservabilitySharedPluginSetup; } export interface ObservabilityAIAssistantPluginStartDependencies { - triggersActionsUi: TriggersAndActionsUIPublicPluginStart; security: SecurityPluginStart; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; + observabilityShared: ObservabilitySharedPluginStart; features: FeaturesPluginStart; } diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/builders.ts b/x-pack/plugins/observability_ai_assistant/public/utils/builders.ts new file mode 100644 index 0000000000000..597d28499f9d2 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/utils/builders.ts @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { uniqueId } from 'lodash'; +import { MessageRole, Conversation, FunctionDefinition } from '../../common/types'; +import { ChatTimelineItem } from '../components/chat/chat_timeline'; +import { getSystemMessage } from '../service/get_system_message'; + +type ChatItemBuildProps = Partial & Pick; + +export function buildChatItem(params: ChatItemBuildProps): ChatTimelineItem { + return { + id: uniqueId(), + title: '', + canEdit: false, + canGiveFeedback: false, + canRegenerate: params.role === MessageRole.User, + currentUser: { + username: 'elastic', + }, + loading: false, + ...params, + }; +} + +export function buildSystemChatItem(params?: Omit) { + return buildChatItem({ + role: MessageRole.System, + ...params, + }); +} + +export function buildChatInitItem() { + return buildChatItem({ + role: MessageRole.User, + title: 'started a conversation', + canRegenerate: false, + }); +} + +export function buildUserChatItem(params?: Omit) { + return buildChatItem({ + role: MessageRole.User, + content: "What's a function?", + canEdit: true, + ...params, + }); +} + +export function buildAssistantChatItem(params?: Omit) { + return buildChatItem({ + role: MessageRole.Assistant, + content: `In computer programming and mathematics, a function is a fundamental concept that represents a relationship between input values and output values. It takes one or more input values (also known as arguments or parameters) and processes them to produce a result, which is the output of the function. The input values are passed to the function, and the function performs a specific set of operations or calculations on those inputs to produce the desired output. + A function is often defined with a name, which serves as an identifier to call and use the function in the code. It can be thought of as a reusable block of code that can be executed whenever needed, and it helps in organizing code and making it more modular and maintainable.`, + canRegenerate: true, + canGiveFeedback: true, + ...params, + }); +} + +export function buildFunctionChatItem(params: Omit) { + return buildChatItem({ + role: MessageRole.User, + title: 'executed a function', + function_call: { + name: 'leftpad', + arguments: '{ foo: "bar" }', + trigger: MessageRole.Assistant, + }, + ...params, + }); +} + +export function buildTimelineItems() { + return { + items: [buildSystemChatItem(), buildUserChatItem(), buildAssistantChatItem()], + }; +} + +export function buildConversation(params?: Partial) { + return { + '@timestamp': '', + user: { + name: 'foo', + }, + conversation: { + id: uniqueId(), + title: '', + last_updated: '', + }, + messages: [getSystemMessage()], + labels: {}, + numeric_labels: {}, + namespace: '', + ...params, + }; +} + +export function buildFunction(): FunctionDefinition { + return { + options: { + name: 'elasticsearch', + contexts: ['core'], + description: 'Call Elasticsearch APIs on behalf of the user', + parameters: { + type: 'object', + properties: { + method: { + type: 'string', + description: 'The HTTP method of the Elasticsearch endpoint', + enum: ['GET', 'PUT', 'POST', 'DELETE', 'PATCH'] as const, + }, + path: { + type: 'string', + description: 'The path of the Elasticsearch endpoint, including query parameters', + }, + }, + required: ['method' as const, 'path' as const], + }, + }, + respond: async (options: { arguments: any }, signal: AbortSignal) => ({}), + }; +} + +export const buildFunctionElasticsearch = buildFunction; + +export function buildFunctionServiceSummary(): FunctionDefinition { + return { + options: { + name: 'get_service_summary', + contexts: ['core'], + description: + 'Gets a summary of a single service, including: the language, service version, deployments, infrastructure, alerting, etc. ', + parameters: { + type: 'object', + }, + }, + respond: async (options: { arguments: any }, signal: AbortSignal) => ({}), + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/get_connectors_management_href.ts b/x-pack/plugins/observability_ai_assistant/public/utils/get_connectors_management_href.ts new file mode 100644 index 0000000000000..7d5456a57e47b --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/utils/get_connectors_management_href.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpStart } from '@kbn/core/public'; + +export function getConnectorsManagementHref(http: HttpStart) { + return http!.basePath.prepend( + `/app/management/insightsAndAlerting/triggersActionsConnectors/connectors` + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.ts b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.ts new file mode 100644 index 0000000000000..3c2ba7da42028 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { v4 } from 'uuid'; +import { i18n } from '@kbn/i18n'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import dedent from 'dedent'; +import { type Message, MessageRole } from '../../common'; +import type { ChatTimelineItem } from '../components/chat/chat_timeline'; + +export function getTimelineItemsfromConversation({ + currentUser, + messages, + hasConnector, +}: { + currentUser?: Pick; + messages: Message[]; + hasConnector: boolean; +}): ChatTimelineItem[] { + return [ + { + id: v4(), + role: MessageRole.User, + title: i18n.translate('xpack.observabilityAiAssistant.conversationStartTitle', { + defaultMessage: 'started a conversation', + }), + canEdit: false, + canGiveFeedback: false, + canRegenerate: false, + loading: false, + currentUser, + }, + ...messages.map((message) => { + const hasFunction = !!message.message.function_call?.name; + const isSystemPrompt = message.message.role === MessageRole.System; + + let title: string; + let content: string | undefined; + + if (hasFunction) { + title = i18n.translate('xpack.observabilityAiAssistant.suggestedFunctionEvent', { + defaultMessage: 'suggested a function', + }); + content = dedent(`I have requested your system performs the function _${ + message.message.function_call?.name + }_ with the payload + \`\`\` + ${JSON.stringify(JSON.parse(message.message.function_call?.arguments || ''), null, 4)} + \`\`\` + and return its results for me to look at.`); + } else if (isSystemPrompt) { + title = i18n.translate('xpack.observabilityAiAssistant.addedSystemPromptEvent', { + defaultMessage: 'added a prompt', + }); + content = ''; + } else { + title = ''; + content = message.message.content; + } + + const props = { + id: v4(), + role: message.message.role, + canEdit: hasConnector && (message.message.role === MessageRole.User || hasFunction), + canRegenerate: hasConnector && message.message.role === MessageRole.Assistant, + canGiveFeedback: message.message.role === MessageRole.Assistant, + loading: false, + title, + content, + currentUser, + }; + + return props; + }), + ]; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/readable_stream_reader_into_observable.ts b/x-pack/plugins/observability_ai_assistant/public/utils/readable_stream_reader_into_observable.ts index f65e0fbd7ee7f..bd9b235d7425d 100644 --- a/x-pack/plugins/observability_ai_assistant/public/utils/readable_stream_reader_into_observable.ts +++ b/x-pack/plugins/observability_ai_assistant/public/utils/readable_stream_reader_into_observable.ts @@ -13,7 +13,7 @@ export function readableStreamReaderIntoObservable( return new Observable((subscriber) => { let lineBuffer: string = ''; - async function read() { + async function read(): Promise { const { done, value } = await readableStreamReader.read(); if (done) { if (lineBuffer) { @@ -35,13 +35,13 @@ export function readableStreamReaderIntoObservable( subscriber.next(line); } - read(); + return read(); } read().catch((err) => subscriber.error(err)); return () => { - readableStreamReader.cancel(); + readableStreamReader.cancel().catch(() => {}); }; }).pipe(share()); } diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx b/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx index 0ef1cfa5ce358..3673bd9879482 100644 --- a/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx @@ -5,7 +5,39 @@ * 2.0. */ import React, { ComponentType } from 'react'; +import { Observable } from 'rxjs'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import type { Serializable } from '@kbn/utility-types'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import { ObservabilityAIAssistantProvider } from '../context/observability_ai_assistant_provider'; +import { ObservabilityAIAssistantAPIClient } from '../api'; +import type { Message } from '../../common'; +import type { ObservabilityAIAssistantService, PendingMessage } from '../types'; +import { buildFunctionElasticsearch, buildFunctionServiceSummary } from './builders'; + +const service: ObservabilityAIAssistantService = { + isEnabled: () => true, + chat: (options: { messages: Message[]; connectorId: string }) => new Observable(), + callApi: {} as ObservabilityAIAssistantAPIClient, + getCurrentUser: async (): Promise => ({ + username: 'user', + roles: [], + enabled: true, + authentication_realm: { name: 'foo', type: '' }, + lookup_realm: { name: 'foo', type: '' }, + authentication_provider: { name: '', type: '' }, + authentication_type: '', + elastic_cloud_user: false, + }), + getContexts: () => [], + getFunctions: () => [buildFunctionElasticsearch(), buildFunctionServiceSummary()], + executeFunction: async ( + name: string, + args: string | undefined, + signal: AbortSignal + ): Promise<{ content?: Serializable; data?: Serializable }> => ({}), + renderFunction: (name: string, response: {}) =>
Hello! {name}
, +}; export function KibanaReactStorybookDecorator(Story: ComponentType) { return ( @@ -21,7 +53,9 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { }, }} > - + + + ); } diff --git a/x-pack/plugins/observability_ai_assistant/server/index.ts b/x-pack/plugins/observability_ai_assistant/server/index.ts index f471407678309..38a2caffca3e4 100644 --- a/x-pack/plugins/observability_ai_assistant/server/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/index.ts @@ -5,11 +5,18 @@ * 2.0. */ -import type { PluginInitializerContext } from '@kbn/core/server'; +import type { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server'; import type { ObservabilityAIAssistantConfig } from './config'; import { ObservabilityAIAssistantPlugin } from './plugin'; export type { ObservabilityAIAssistantServerRouteRepository } from './routes/get_global_observability_ai_assistant_route_repository'; +import { config as configSchema } from './config'; + +export const config: PluginConfigDescriptor = { + exposeToBrowser: {}, + schema: configSchema, +}; + export const plugin = (ctx: PluginInitializerContext) => new ObservabilityAIAssistantPlugin(ctx); diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts index 5e0744a7f7238..30da7d10fed91 100644 --- a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts +++ b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts @@ -19,6 +19,14 @@ const chatRoute = createObservabilityAIAssistantServerRoute({ body: t.type({ messages: t.array(messageRt), connectorId: t.string, + functions: t.array( + t.type({ + name: t.string, + description: t.string, + parameters: t.any, + contexts: t.array(t.string), + }) + ), }), }), handler: async (resources): Promise => { @@ -30,9 +38,14 @@ const chatRoute = createObservabilityAIAssistantServerRoute({ throw notImplemented(); } + const { + body: { messages, connectorId, functions }, + } = params; + return client.chat({ - messages: params.body.messages, - connectorId: params.body.connectorId, + messages, + connectorId, + functions, }); }, }); diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/conversations/route.ts b/x-pack/plugins/observability_ai_assistant/server/routes/conversations/route.ts index a5f6d637d9a59..625594cc74578 100644 --- a/x-pack/plugins/observability_ai_assistant/server/routes/conversations/route.ts +++ b/x-pack/plugins/observability_ai_assistant/server/routes/conversations/route.ts @@ -21,7 +21,7 @@ const getConversationRoute = createObservabilityAIAssistantServerRoute({ options: { tags: ['access:ai_assistant'], }, - handler: async (resources): Promise => { + handler: async (resources): Promise => { const { service, request, params } = resources; const client = await service.getClient({ request }); diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/functions/route.ts b/x-pack/plugins/observability_ai_assistant/server/routes/functions/route.ts new file mode 100644 index 0000000000000..e9b4426171f63 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/server/routes/functions/route.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { nonEmptyStringRt, toBooleanRt } from '@kbn/io-ts-utils'; +import { notImplemented } from '@hapi/boom'; +import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route'; +import { KnowledgeBaseEntry } from '../../../common/types'; + +const functionElasticsearchRoute = createObservabilityAIAssistantServerRoute({ + endpoint: 'POST /internal/observability_ai_assistant/functions/elasticsearch', + options: { + tags: ['access:ai_assistant'], + }, + params: t.type({ + body: t.intersection([ + t.type({ + method: t.union([ + t.literal('GET'), + t.literal('POST'), + t.literal('PATCH'), + t.literal('PUT'), + t.literal('DELETE'), + ]), + path: t.string, + }), + t.partial({ + body: t.any, + }), + ]), + }), + handler: async (resources): Promise => { + const { method, path, body } = resources.params.body; + + const response = await ( + await resources.context.core + ).elasticsearch.client.asCurrentUser.transport.request({ + method, + path, + body, + }); + + return response; + }, +}); + +const functionRecallRoute = createObservabilityAIAssistantServerRoute({ + endpoint: 'POST /internal/observability_ai_assistant/functions/recall', + params: t.type({ + body: t.type({ + query: nonEmptyStringRt, + }), + }), + options: { + tags: ['access:ai_assistant'], + }, + handler: async (resources): Promise<{ entries: KnowledgeBaseEntry[] }> => { + const client = await resources.service.getClient({ request: resources.request }); + + if (!client) { + throw notImplemented(); + } + + return client.recall(resources.params.body.query); + }, +}); + +const functionSummariseRoute = createObservabilityAIAssistantServerRoute({ + endpoint: 'POST /internal/observability_ai_assistant/functions/summarise', + params: t.type({ + body: t.type({ + id: t.string, + text: nonEmptyStringRt, + confidence: t.union([t.literal('low'), t.literal('medium'), t.literal('high')]), + is_correction: toBooleanRt, + public: toBooleanRt, + }), + }), + options: { + tags: ['access:ai_assistant'], + }, + handler: async (resources): Promise => { + const client = await resources.service.getClient({ request: resources.request }); + + if (!client) { + throw notImplemented(); + } + + const { + confidence, + id, + is_correction: isCorrection, + text, + public: isPublic, + } = resources.params.body; + + return client.summarise({ + entry: { + confidence, + id, + is_correction: isCorrection, + text, + public: isPublic, + }, + }); + }, +}); + +const setupKnowledgeBaseRoute = createObservabilityAIAssistantServerRoute({ + endpoint: 'POST /internal/observability_ai_assistant/functions/setup_kb', + options: { + tags: ['access:ai_assistant'], + }, + handler: async (resources): Promise => { + const client = await resources.service.getClient({ request: resources.request }); + + if (!client) { + throw notImplemented(); + } + + await client.setupKnowledgeBase(); + }, +}); + +export const functionRoutes = { + ...functionElasticsearchRoute, + ...functionRecallRoute, + ...functionSummariseRoute, + ...setupKnowledgeBaseRoute, +}; diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/get_global_observability_ai_assistant_route_repository.ts b/x-pack/plugins/observability_ai_assistant/server/routes/get_global_observability_ai_assistant_route_repository.ts index fcca8df0f03e7..a01033137600b 100644 --- a/x-pack/plugins/observability_ai_assistant/server/routes/get_global_observability_ai_assistant_route_repository.ts +++ b/x-pack/plugins/observability_ai_assistant/server/routes/get_global_observability_ai_assistant_route_repository.ts @@ -8,12 +8,14 @@ import { chatRoutes } from './chat/route'; import { connectorRoutes } from './connectors/route'; import { conversationRoutes } from './conversations/route'; +import { functionRoutes } from './functions/route'; export function getGlobalObservabilityAIAssistantServerRouteRepository() { return { ...chatRoutes, ...conversationRoutes, ...connectorRoutes, + ...functionRoutes, }; } diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/runtime_types.ts b/x-pack/plugins/observability_ai_assistant/server/routes/runtime_types.ts index 49dcb8ec1a930..41d0d9d19492a 100644 --- a/x-pack/plugins/observability_ai_assistant/server/routes/runtime_types.ts +++ b/x-pack/plugins/observability_ai_assistant/server/routes/runtime_types.ts @@ -5,6 +5,7 @@ * 2.0. */ import * as t from 'io-ts'; +import { toBooleanRt } from '@kbn/io-ts-utils'; import { Conversation, ConversationCreateRequest, @@ -23,7 +24,6 @@ export const messageRt: t.Type = t.type({ role: t.union([ t.literal(MessageRole.System), t.literal(MessageRole.Assistant), - t.literal(MessageRole.Event), t.literal(MessageRole.Function), t.literal(MessageRole.User), t.literal(MessageRole.Elastic), @@ -32,6 +32,7 @@ export const messageRt: t.Type = t.type({ t.partial({ content: t.string, name: t.string, + event: t.string, function_call: t.intersection([ t.type({ name: t.string, @@ -42,7 +43,7 @@ export const messageRt: t.Type = t.type({ ]), }), t.partial({ - args: serializeableRt, + arguments: serializeableRt, data: serializeableRt, }), ]), @@ -58,6 +59,7 @@ export const baseConversationRt: t.Type = t.type({ messages: t.array(messageRt), labels: t.record(t.string, t.string), numeric_labels: t.record(t.string, t.number), + public: toBooleanRt, }); export const conversationCreateRt: t.Type = t.intersection([ diff --git a/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts b/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts index 30e47cc34672d..1218cc29d409e 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts @@ -4,29 +4,41 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { v4 } from 'uuid'; - -import type { ChatCompletionRequestMessage, CreateChatCompletionRequest } from 'openai'; -import type { IncomingMessage } from 'http'; +import { errors } from '@elastic/elasticsearch'; +import type { QueryDslTextExpansionQuery, SearchHit } from '@elastic/elasticsearch/lib/api/types'; +import { internal, notFound, serverUnavailable } from '@hapi/boom'; +import type { ActionsClient } from '@kbn/actions-plugin/server/actions_client'; import type { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; -import type { ActionsClient } from '@kbn/actions-plugin/server/actions_client'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import { internal, notFound } from '@hapi/boom'; +import type { IncomingMessage } from 'http'; import { compact, isEmpty, merge, omit } from 'lodash'; -import type { SearchHit } from '@elastic/elasticsearch/lib/api/types'; +import type { + ChatCompletionFunctions, + ChatCompletionRequestMessage, + CreateChatCompletionRequest, +} from 'openai'; +import { v4 } from 'uuid'; import { + KnowledgeBaseEntry, + MessageRole, type Conversation, type ConversationCreateRequest, type ConversationUpdateRequest, + type FunctionDefinition, type Message, - MessageRole, } from '../../../common/types'; import type { IObservabilityAIAssistantClient, ObservabilityAIAssistantResourceNames, } from '../types'; +const ELSER_MODEL_ID = '.elser_model_1'; + +function throwKnowledgeBaseNotReady(body: any) { + throw serverUnavailable(`Knowledge base is not ready yet`, body); +} + export class ObservabilityAIAssistantClient implements IObservabilityAIAssistantClient { constructor( private readonly dependencies: { @@ -48,8 +60,20 @@ export class ObservabilityAIAssistantClient implements IObservabilityAIAssistant bool: { filter: [ { - term: { - 'user.name': this.dependencies.user.name, + bool: { + should: [ + { + term: { + 'user.name': this.dependencies.user.name, + }, + }, + { + term: { + public: true, + }, + }, + ], + minimum_should_match: 1, }, }, { @@ -90,8 +114,13 @@ export class ObservabilityAIAssistantClient implements IObservabilityAIAssistant }; }; - get = async (conversationId: string): Promise => { - return (await this.getConversationWithMetaFields(conversationId))?._source; + get = async (conversationId: string): Promise => { + const conversation = await this.getConversationWithMetaFields(conversationId); + + if (!conversation) { + throw notFound(); + } + return conversation._source!; }; delete = async (conversationId: string): Promise => { @@ -104,38 +133,46 @@ export class ObservabilityAIAssistantClient implements IObservabilityAIAssistant await this.dependencies.esClient.delete({ id: conversation._id, index: conversation._index, + refresh: 'wait_for', }); }; chat = async ({ messages, connectorId, + functions, }: { messages: Message[]; connectorId: string; + functions: Array; }): Promise => { const messagesForOpenAI: ChatCompletionRequestMessage[] = compact( - messages.map((message) => { - if (message.message.role === MessageRole.Event) { - return undefined; - } - const role = - message.message.role === MessageRole.Elastic ? MessageRole.User : message.message.role; - - return { - role, - content: message.message.content, - function_call: isEmpty(message.message.function_call?.name) - ? undefined - : omit(message.message.function_call, 'trigger'), - name: message.message.name, - }; - }) + messages + .filter((message) => message.message.content || message.message.function_call?.name) + .map((message) => { + const role = + message.message.role === MessageRole.Elastic ? MessageRole.User : message.message.role; + + return { + role, + content: message.message.content, + function_call: isEmpty(message.message.function_call?.name) + ? undefined + : omit(message.message.function_call, 'trigger'), + name: message.message.name, + }; + }) + ); + + const functionsForOpenAI: ChatCompletionFunctions[] = functions.map((fn) => + omit(fn, 'contexts') ); const request: Omit & { model?: string } = { messages: messagesForOpenAI, stream: true, + functions: functionsForOpenAI, + temperature: 0, }; const executeResult = await this.dependencies.actionsClient.execute({ @@ -193,6 +230,7 @@ export class ObservabilityAIAssistantClient implements IObservabilityAIAssistant id: document._id, index: document._index, doc: updatedConversation, + refresh: 'wait_for', }); return updatedConversation; @@ -214,8 +252,110 @@ export class ObservabilityAIAssistantClient implements IObservabilityAIAssistant await this.dependencies.esClient.index({ index: this.dependencies.resources.aliases.conversations, document: createdConversation, + refresh: 'wait_for', }); return createdConversation; }; + + recall = async (query: string): Promise<{ entries: KnowledgeBaseEntry[] }> => { + try { + const response = await this.dependencies.esClient.search({ + index: this.dependencies.resources.aliases.kb, + query: { + bool: { + should: [ + { + text_expansion: { + 'ml.tokens': { + model_text: query, + model_id: '.elser_model_1', + }, + } as unknown as QueryDslTextExpansionQuery, + }, + ], + filter: [...this.getAccessQuery()], + }, + }, + _source: { + excludes: ['ml.tokens'], + }, + }); + + return { entries: response.hits.hits.map((hit) => hit._source!) }; + } catch (error) { + if ( + (error instanceof errors.ResponseError && + error.body.error.type === 'resource_not_found_exception') || + error.body.error.type === 'status_exception' + ) { + throwKnowledgeBaseNotReady(error.body); + } + throw error; + } + }; + + summarise = async ({ + entry: { id, ...document }, + }: { + entry: Omit; + }): Promise => { + try { + await this.dependencies.esClient.index({ + index: this.dependencies.resources.aliases.kb, + id, + document: { + '@timestamp': new Date().toISOString(), + ...document, + user: this.dependencies.user, + namespace: this.dependencies.namespace, + }, + pipeline: this.dependencies.resources.pipelines.kb, + }); + } catch (error) { + if (error instanceof errors.ResponseError && error.body.error.type === 'status_exception') { + throwKnowledgeBaseNotReady(error.body); + } + throw error; + } + }; + + setupKnowledgeBase = async () => { + // if this fails, it's fine to propagate the error to the user + await this.dependencies.esClient.ml.putTrainedModel({ + model_id: ELSER_MODEL_ID, + input: { + field_names: ['text_field'], + }, + }); + + try { + await this.dependencies.esClient.ml.startTrainedModelDeployment({ + model_id: ELSER_MODEL_ID, + }); + + const modelStats = await this.dependencies.esClient.ml.getTrainedModelsStats({ + model_id: ELSER_MODEL_ID, + }); + + const elserModelStats = modelStats.trained_model_stats[0]; + + if (elserModelStats?.deployment_stats?.state !== 'started') { + throwKnowledgeBaseNotReady({ + message: `Deployment has not started`, + deployment_stats: elserModelStats.deployment_stats, + }); + } + return; + } catch (error) { + if ( + (error instanceof errors.ResponseError && + error.body.error.type === 'resource_not_found_exception') || + error.body.error.type === 'status_exception' + ) { + throwKnowledgeBaseNotReady(error.body); + } + throw error; + } + }; } diff --git a/x-pack/plugins/observability_ai_assistant/server/service/conversation_component_template.ts b/x-pack/plugins/observability_ai_assistant/server/service/conversation_component_template.ts index b758d702d2c43..c00e2c5e3a1fb 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/conversation_component_template.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/conversation_component_template.ts @@ -66,6 +66,7 @@ export const conversationComponentTemplate: ClusterComponentTemplate['component_ type: 'object', properties: { content: text, + event: text, role: keyword, data: { type: 'object', @@ -75,7 +76,7 @@ export const conversationComponentTemplate: ClusterComponentTemplate['component_ type: 'object', properties: { name: keyword, - args: { + arguments: { type: 'object', enabled: false, }, @@ -86,6 +87,9 @@ export const conversationComponentTemplate: ClusterComponentTemplate['component_ }, }, }, + public: { + type: 'boolean', + }, }, }, }; diff --git a/x-pack/plugins/observability_ai_assistant/server/service/index.ts b/x-pack/plugins/observability_ai_assistant/server/service/index.ts index 5896a56bd634c..5a76bd9125797 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/index.ts @@ -14,6 +14,7 @@ import { getSpaceIdFromPath } from '@kbn/spaces-plugin/common'; import { once } from 'lodash'; import { ObservabilityAIAssistantClient } from './client'; import { conversationComponentTemplate } from './conversation_component_template'; +import { kbComponentTemplate } from './kb_component_template'; import type { IObservabilityAIAssistantClient, IObservabilityAIAssistantService, @@ -31,19 +32,26 @@ export class ObservabilityAIAssistantService implements IObservabilityAIAssistan private readonly resourceNames: ObservabilityAIAssistantResourceNames = { componentTemplate: { conversations: getResourceName('component-template-conversations'), + kb: getResourceName('component-template-kb'), }, aliases: { conversations: getResourceName('conversations'), + kb: getResourceName('kb'), }, indexPatterns: { conversations: getResourceName('conversations*'), + kb: getResourceName('kb*'), }, indexTemplate: { conversations: getResourceName('index-template-conversations'), + kb: getResourceName('index-template-kb'), }, ilmPolicy: { conversations: getResourceName('ilm-policy-conversations'), }, + pipelines: { + kb: getResourceName('kb-ingest-pipeline'), + }, }; constructor({ logger, core }: { logger: Logger; core: CoreSetup }) { @@ -94,20 +102,78 @@ export class ObservabilityAIAssistantService implements IObservabilityAIAssistan }, }); - const aliasName = this.resourceNames.aliases.conversations; + const conversationAliasName = this.resourceNames.aliases.conversations; await createConcreteWriteIndex({ esClient, logger: this.logger, totalFieldsLimit: 10000, indexPatterns: { - alias: aliasName, - pattern: `${aliasName}*`, - basePattern: `${aliasName}*`, - name: `${aliasName}-000001`, + alias: conversationAliasName, + pattern: `${conversationAliasName}*`, + basePattern: `${conversationAliasName}*`, + name: `${conversationAliasName}-000001`, template: this.resourceNames.indexTemplate.conversations, }, }); + + await esClient.cluster.putComponentTemplate({ + create: false, + name: this.resourceNames.componentTemplate.kb, + template: kbComponentTemplate, + }); + + await esClient.ingest.putPipeline({ + id: this.resourceNames.pipelines.kb, + processors: [ + { + inference: { + model_id: '.elser_model_1', + target_field: 'ml', + field_map: { + text: 'text_field', + }, + inference_config: { + // @ts-expect-error + text_expansion: { + results_field: 'tokens', + }, + }, + }, + }, + ], + }); + + await esClient.indices.putIndexTemplate({ + name: this.resourceNames.indexTemplate.kb, + composed_of: [this.resourceNames.componentTemplate.kb], + create: false, + index_patterns: [this.resourceNames.indexPatterns.kb], + template: { + settings: { + number_of_shards: 1, + auto_expand_replicas: '0-1', + refresh_interval: '1s', + }, + }, + }); + + const kbAliasName = this.resourceNames.aliases.kb; + + await createConcreteWriteIndex({ + esClient, + logger: this.logger, + totalFieldsLimit: 10000, + indexPatterns: { + alias: kbAliasName, + pattern: `${kbAliasName}*`, + basePattern: `${kbAliasName}*`, + name: `${kbAliasName}-000001`, + template: this.resourceNames.indexTemplate.kb, + }, + }); + + this.logger.info('Successfully set up index assets'); } catch (error) { this.logger.error(`Failed to initialize service: ${error.message}`); this.logger.debug(error); diff --git a/x-pack/plugins/observability_ai_assistant/server/service/kb_component_template.ts b/x-pack/plugins/observability_ai_assistant/server/service/kb_component_template.ts new file mode 100644 index 0000000000000..55d6bbd15519c --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/server/service/kb_component_template.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ClusterComponentTemplate } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +const keyword = { + type: 'keyword' as const, + ignore_above: 1024, +}; + +const text = { + type: 'text' as const, +}; + +const date = { + type: 'date' as const, +}; + +export const kbComponentTemplate: ClusterComponentTemplate['component_template']['template'] = { + mappings: { + dynamic: false, + properties: { + '@timestamp': date, + id: keyword, + user: { + properties: { + id: keyword, + name: keyword, + }, + }, + conversation: { + properties: { + id: keyword, + title: text, + last_updated: date, + }, + }, + namespace: keyword, + text, + 'ml.tokens': { + type: 'rank_features', + }, + confidence: keyword, + is_correction: { + type: 'boolean', + }, + public: { + type: 'boolean', + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_ai_assistant/server/service/types.ts b/x-pack/plugins/observability_ai_assistant/server/service/types.ts index d8f0c6b2b5f09..265f0c695d446 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/types.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/types.ts @@ -7,20 +7,29 @@ import { IncomingMessage } from 'http'; import { KibanaRequest } from '@kbn/core/server'; -import { +import type { Conversation, ConversationCreateRequest, ConversationUpdateRequest, + FunctionDefinition, + KnowledgeBaseEntry, Message, } from '../../common/types'; export interface IObservabilityAIAssistantClient { - chat: (options: { messages: Message[]; connectorId: string }) => Promise; - get: (conversationId: string) => void; + chat: (options: { + messages: Message[]; + connectorId: string; + functions: Array; + }) => Promise; + get: (conversationId: string) => Promise; find: (options?: { query?: string }) => Promise<{ conversations: Conversation[] }>; create: (conversation: ConversationCreateRequest) => Promise; update: (conversation: ConversationUpdateRequest) => Promise; delete: (conversationId: string) => Promise; + recall: (query: string) => Promise<{ entries: KnowledgeBaseEntry[] }>; + summarise: (options: { entry: Omit }) => Promise; + setupKnowledgeBase: () => Promise; } export interface IObservabilityAIAssistantService { @@ -32,17 +41,24 @@ export interface IObservabilityAIAssistantService { export interface ObservabilityAIAssistantResourceNames { componentTemplate: { conversations: string; + kb: string; }; indexTemplate: { conversations: string; + kb: string; }; ilmPolicy: { conversations: string; }; aliases: { conversations: string; + kb: string; }; indexPatterns: { conversations: string; + kb: string; + }; + pipelines: { + kb: string; }; } diff --git a/x-pack/plugins/observability_ai_assistant/tsconfig.json b/x-pack/plugins/observability_ai_assistant/tsconfig.json index 39507261898f5..152f4a3723465 100644 --- a/x-pack/plugins/observability_ai_assistant/tsconfig.json +++ b/x-pack/plugins/observability_ai_assistant/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../../tsconfig.base.json", "compilerOptions": { - "outDir": "target/types", + "outDir": "target/types" }, "include": [ "../../../typings/**/*", @@ -25,9 +25,17 @@ "@kbn/kibana-react-plugin", "@kbn/shared-ux-utility", "@kbn/alerting-plugin", + "@kbn/shared-ux-link-redirect-app", + "@kbn/typed-react-router-config", + "@kbn/ui-theme", + "@kbn/user-profile-components", + "@kbn/observability-shared-plugin", + "@kbn/kibana-utils-plugin", + "@kbn/monaco", + "@kbn/io-ts-utils", + "@kbn/std", + "@kbn/alerting-plugin", "@kbn/features-plugin" ], - "exclude": [ - "target/**/*", - ] + "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx b/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx index 4151655c5a2bd..21c6a08815b76 100644 --- a/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx +++ b/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx @@ -19,6 +19,7 @@ export function useGetUserCasesPermissions() { update: false, delete: false, push: false, + connectors: false, }); const uiCapabilities = useKibana().services.application!.capabilities; @@ -35,6 +36,7 @@ export function useGetUserCasesPermissions() { update: casesCapabilities.update, delete: casesCapabilities.delete, push: casesCapabilities.push, + connectors: casesCapabilities.connectors, }); }, [ casesCapabilities.all, @@ -43,6 +45,7 @@ export function useGetUserCasesPermissions() { casesCapabilities.update, casesCapabilities.delete, casesCapabilities.push, + casesCapabilities.connectors, ]); return casesPermissions; diff --git a/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts b/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts index 2b3ff9cfbaf54..a0b6a8aed95b0 100644 --- a/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts +++ b/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts @@ -12,4 +12,5 @@ export const noCasesPermissions = () => ({ update: false, delete: false, push: false, + connectors: false, }); diff --git a/x-pack/plugins/rollup/kibana.jsonc b/x-pack/plugins/rollup/kibana.jsonc index 7bb5740ff8b2b..73f0e76b16d73 100644 --- a/x-pack/plugins/rollup/kibana.jsonc +++ b/x-pack/plugins/rollup/kibana.jsonc @@ -13,13 +13,14 @@ "requiredPlugins": [ "management", "licensing", - "features" + "features", + "dataViews", + "data" ], "optionalPlugins": [ "home", "indexManagement", - "usageCollection", - "visTypeTimeseries" + "usageCollection" ], "requiredBundles": [ "kibanaUtils", diff --git a/x-pack/plugins/rollup/server/plugin.ts b/x-pack/plugins/rollup/server/plugin.ts index 06416685ab508..409c730385db9 100644 --- a/x-pack/plugins/rollup/server/plugin.ts +++ b/x-pack/plugins/rollup/server/plugin.ts @@ -30,8 +30,8 @@ export class RollupPlugin implements Plugin { } public setup( - { http, uiSettings, savedObjects, getStartServices }: CoreSetup, - { features, licensing, indexManagement, visTypeTimeseries, usageCollection }: Dependencies + { http, uiSettings, getStartServices }: CoreSetup, + { features, licensing, indexManagement, usageCollection, dataViews, data }: Dependencies ) { this.license.setup( { @@ -103,6 +103,8 @@ export class RollupPlugin implements Plugin { if (indexManagement && indexManagement.indexDataEnricher) { indexManagement.indexDataEnricher.add(rollupDataEnricher); } + dataViews.enableRollups(); + data.search.enableRollups(); } start() {} diff --git a/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts b/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts index 99312936adba4..c32e0e2be8f5f 100644 --- a/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts @@ -18,6 +18,7 @@ export const registerGetRoute = ({ }: RouteDependencies) => { router.get( { + // this endpoint is used by the data views plugin, see https://github.com/elastic/kibana/issues/152708 path: addBasePath('/indices'), validate: false, }, diff --git a/x-pack/plugins/rollup/server/types.ts b/x-pack/plugins/rollup/server/types.ts index 177efe0915fcf..29d2fe2e99771 100644 --- a/x-pack/plugins/rollup/server/types.ts +++ b/x-pack/plugins/rollup/server/types.ts @@ -12,6 +12,8 @@ import { VisTypeTimeseriesSetup } from '@kbn/vis-type-timeseries-plugin/server'; import { getCapabilitiesForRollupIndices } from '@kbn/data-plugin/server'; import { IndexManagementPluginSetup } from '@kbn/index-management-plugin/server'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; +import { DataViewsServerPluginSetup } from '@kbn/data-views-plugin/server'; +import { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { License } from './services'; import { IndexPatternsFetcher } from './shared_imports'; @@ -24,6 +26,8 @@ export interface Dependencies { usageCollection?: UsageCollectionSetup; licensing: LicensingPluginSetup; features: FeaturesPluginSetup; + dataViews: DataViewsServerPluginSetup; + data: DataPluginSetup; } export interface RouteDependencies { diff --git a/x-pack/plugins/rollup/tsconfig.json b/x-pack/plugins/rollup/tsconfig.json index 151c5151a0c17..366b44b2c33be 100644 --- a/x-pack/plugins/rollup/tsconfig.json +++ b/x-pack/plugins/rollup/tsconfig.json @@ -32,6 +32,7 @@ "@kbn/i18n-react", "@kbn/config-schema", "@kbn/shared-ux-router", + "@kbn/data-views-plugin", ], "exclude": [ diff --git a/x-pack/plugins/security/common/index.ts b/x-pack/plugins/security/common/index.ts index f7ee13c610a45..c547833949ded 100644 --- a/x-pack/plugins/security/common/index.ts +++ b/x-pack/plugins/security/common/index.ts @@ -23,7 +23,6 @@ export type { UserProfileWithSecurity, UserProfileData, UserProfileLabels, - UserProfileAvatarData, UserProfileUserInfoWithSecurity, ApiKey, UserRealm, diff --git a/x-pack/plugins/security/common/model/index.ts b/x-pack/plugins/security/common/model/index.ts index 822d6036efc06..c8505a644503f 100644 --- a/x-pack/plugins/security/common/model/index.ts +++ b/x-pack/plugins/security/common/model/index.ts @@ -22,7 +22,6 @@ export type { UserProfileData, UserProfileLabels, UserProfileUserInfoWithSecurity, - UserProfileAvatarData, } from './user_profile'; export { getUserAvatarColor, diff --git a/x-pack/plugins/security/common/model/user_profile.ts b/x-pack/plugins/security/common/model/user_profile.ts index c4dd6addd51fc..152b0d0266bbe 100644 --- a/x-pack/plugins/security/common/model/user_profile.ts +++ b/x-pack/plugins/security/common/model/user_profile.ts @@ -7,6 +7,8 @@ import { VISUALIZATION_COLORS } from '@elastic/eui'; +import type { UserProfileAvatarData } from '@kbn/user-profile-components'; + import type { AuthenticatedUser } from './authenticated_user'; import { getUserDisplayName } from './user'; @@ -72,33 +74,6 @@ export type UserProfileData = Record; */ export type UserProfileLabels = Record; -/** - * Avatar stored in user profile. - */ -export interface UserProfileAvatarData { - /** - * Optional initials (two letters) of the user to use as avatar if avatar picture isn't specified. - */ - initials?: string; - /** - * Background color of the avatar when initials are used. - */ - color?: string; - /** - * Base64 data URL for the user avatar image. - */ - imageUrl?: string | null; -} - -export type DarkModeValue = '' | 'dark' | 'light'; - -/** - * User settings stored in the data object of the User Profile - */ -export interface UserSettingsData { - darkMode?: DarkModeValue; -} - /** * Extended user information returned in user profile (both basic and security related properties). */ diff --git a/x-pack/plugins/security/public/account_management/account_management_app.tsx b/x-pack/plugins/security/public/account_management/account_management_app.tsx index 29722c10ea84d..a5b98d66d46ff 100644 --- a/x-pack/plugins/security/public/account_management/account_management_app.tsx +++ b/x-pack/plugins/security/public/account_management/account_management_app.tsx @@ -21,8 +21,13 @@ import type { import { AppNavLinkStatus } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { + KibanaContextProvider, + KibanaThemeProvider, + toMountPoint, +} from '@kbn/kibana-react-plugin/public'; import { Router } from '@kbn/shared-ux-router'; +import { UserProfilesKibanaProvider } from '@kbn/user-profile-components'; import type { AuthenticationServiceSetup } from '../authentication'; import type { SecurityApiClients } from '../components'; @@ -96,7 +101,17 @@ export const Providers: FunctionComponent = ({ - {children} + + + {children} + + diff --git a/x-pack/plugins/security/public/account_management/index.ts b/x-pack/plugins/security/public/account_management/index.ts index eca7287537318..e1a4957aa71e7 100644 --- a/x-pack/plugins/security/public/account_management/index.ts +++ b/x-pack/plugins/security/public/account_management/index.ts @@ -11,5 +11,4 @@ export type { UserProfileBulkGetParams, UserProfileGetCurrentParams, UserProfileSuggestParams, - UpdateUserProfileHook, } from './user_profile'; diff --git a/x-pack/plugins/security/public/account_management/user_profile/index.ts b/x-pack/plugins/security/public/account_management/user_profile/index.ts index 93a1c7d04d315..ed34d7d4a4339 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/index.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/index.ts @@ -13,5 +13,3 @@ export type { UserProfileBulkGetParams, UserProfileSuggestParams, } from './user_profile_api_client'; - -export type { UpdateUserProfileHook } from './use_update_user_profile'; diff --git a/x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx deleted file mode 100644 index 2dafa61496fe8..0000000000000 --- a/x-pack/plugins/security/public/account_management/user_profile/use_update_user_profile.tsx +++ /dev/null @@ -1,150 +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 { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React, { useCallback, useRef, useState } from 'react'; -import useObservable from 'react-use/lib/useObservable'; - -import type { NotificationsStart, ToastInput, ToastOptions } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; - -import type { UserProfileData } from './user_profile'; -import type { UserProfileAPIClient } from './user_profile_api_client'; - -interface Deps { - apiClient: UserProfileAPIClient; - notifications: NotificationsStart; -} - -interface Props { - notificationSuccess?: { - /** Flag to indicate if a notification is shown after update. Default: `true` */ - enabled?: boolean; - /** Customize the title of the notification */ - title?: string; - /** Customize the "page reload needed" text of the notification */ - pageReloadText?: string; - }; - /** Predicate to indicate if the update requires a page reload */ - pageReloadChecker?: ( - previsous: UserProfileData | null | undefined, - next: UserProfileData - ) => boolean; -} - -export type UpdateUserProfileHook = (props?: Props) => { - /** Update the user profile */ - update: (data: UserProfileData) => void; - /** Handler to show a notification after the user profile has been updated */ - showSuccessNotification: (props: { isRefreshRequired: boolean }) => void; - /** Flag to indicate if currently updating */ - isLoading: boolean; - /** The current user profile data */ - userProfileData?: UserProfileData | null; -}; - -const i18nTexts = { - notificationSuccess: { - title: i18n.translate('xpack.security.accountManagement.userProfile.submitSuccessTitle', { - defaultMessage: 'Profile updated', - }), - pageReloadText: i18n.translate( - 'xpack.security.accountManagement.userProfile.requiresPageReloadToastDescription', - { - defaultMessage: 'One or more settings require you to reload the page to take effect.', - } - ), - }, -}; - -export const getUseUpdateUserProfile = ({ apiClient, notifications }: Deps) => { - const { userProfile$ } = apiClient; - - const useUpdateUserProfile = ({ notificationSuccess = {}, pageReloadChecker }: Props = {}) => { - const { - enabled: notificationSuccessEnabled = true, - title: notificationTitle = i18nTexts.notificationSuccess.title, - pageReloadText = i18nTexts.notificationSuccess.pageReloadText, - } = notificationSuccess; - const [isLoading, setIsLoading] = useState(false); - const userProfileData = useObservable(userProfile$); - // Keep a snapshot before updating the user profile so we can compare previous and updated values - const userProfileSnapshot = useRef(); - - const showSuccessNotification = useCallback( - ({ isRefreshRequired = false }: { isRefreshRequired?: boolean } = {}) => { - let successToastInput: ToastInput = { - title: notificationTitle, - }; - let successToastOptions: ToastOptions = {}; - - if (isRefreshRequired) { - successToastOptions = { - toastLifeTimeMs: 1000 * 60 * 5, - }; - - successToastInput = { - ...successToastInput, - text: toMountPoint( - - -

{pageReloadText}

- window.location.reload()} - data-test-subj="windowReloadButton" - > - {i18n.translate( - 'xpack.security.accountManagement.userProfile.requiresPageReloadToastButtonLabel', - { - defaultMessage: 'Reload page', - } - )} - -
-
- ), - }; - } - - notifications.toasts.addSuccess(successToastInput, successToastOptions); - }, - [notificationTitle, pageReloadText] - ); - - const onUserProfileUpdate = useCallback( - (updatedData: UserProfileData) => { - setIsLoading(false); - - if (notificationSuccessEnabled) { - const isRefreshRequired = pageReloadChecker?.(userProfileSnapshot.current, updatedData); - showSuccessNotification({ isRefreshRequired }); - } - }, - [notificationSuccessEnabled, showSuccessNotification, pageReloadChecker] - ); - - const update = useCallback( - (udpatedData: D) => { - userProfileSnapshot.current = userProfileData; - setIsLoading(true); - return apiClient.update(udpatedData).then(() => onUserProfileUpdate(udpatedData)); - }, - [onUserProfileUpdate, userProfileData] - ); - - return { - update, - showSuccessNotification, - userProfileData, - isLoading, - }; - }; - - return useUpdateUserProfile; -}; diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx index 2632f73e99d07..a6227baee4061 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx @@ -39,7 +39,8 @@ import type { CoreStart, IUiSettingsClient } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { UserAvatar } from '@kbn/user-profile-components'; +import type { DarkModeValue, UserProfileData } from '@kbn/user-profile-components'; +import { UserAvatar, useUpdateUserProfile } from '@kbn/user-profile-components'; import type { AuthenticatedUser } from '../../../common'; import { @@ -48,11 +49,6 @@ import { getUserAvatarColor, getUserAvatarInitials, } from '../../../common/model'; -import type { - DarkModeValue, - UserProfileAvatarData, - UserSettingsData, -} from '../../../common/model/user_profile'; import { useSecurityApiClients } from '../../components'; import { Breadcrumb } from '../../components/breadcrumb'; import { @@ -65,15 +61,8 @@ import { FormLabel } from '../../components/form_label'; import { FormRow, OptionalText } from '../../components/form_row'; import { ChangePasswordModal } from '../../management/users/edit_user/change_password_modal'; import { isUserReserved } from '../../management/users/user_utils'; -import { getUseUpdateUserProfile } from './use_update_user_profile'; import { createImageHandler, getRandomColor, IMAGE_FILE_TYPES, VALID_HEX_COLOR } from './utils'; -export interface UserProfileData { - avatar?: UserProfileAvatarData; - userSettings?: UserSettingsData; - [key: string]: unknown; -} - export interface UserProfileProps { user: AuthenticatedUser; data?: UserProfileData; @@ -833,12 +822,11 @@ export const UserProfile: FunctionComponent = ({ user, data }) export function useUserProfileForm({ user, data }: UserProfileProps) { const { services } = useKibana(); - const { userProfiles, users } = useSecurityApiClients(); + const { users } = useSecurityApiClients(); - const { update, showSuccessNotification } = getUseUpdateUserProfile({ - apiClient: userProfiles, - notifications: services.notifications, - })({ notificationSuccess: { enabled: false } }); + const { update, showSuccessNotification } = useUpdateUserProfile({ + notificationSuccess: { enabled: false }, + }); const [initialValues, resetInitialValues] = useState({ user: { diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts index 4b992f616ca14..4760aa15ab0b3 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts @@ -10,9 +10,9 @@ import type { Observable } from 'rxjs'; import { BehaviorSubject, Subject } from 'rxjs'; import type { HttpStart } from '@kbn/core/public'; +import type { UserProfileData } from '@kbn/user-profile-components'; import type { GetUserProfileResponse, UserProfile } from '../../../common'; -import type { UserProfileData } from './user_profile'; /** * Parameters for the get user profile for the current user API. diff --git a/x-pack/plugins/security/public/index.ts b/x-pack/plugins/security/public/index.ts index b51bb3d25092e..209bc5ff576b6 100644 --- a/x-pack/plugins/security/public/index.ts +++ b/x-pack/plugins/security/public/index.ts @@ -24,7 +24,6 @@ export type { UserProfileBulkGetParams, UserProfileGetCurrentParams, UserProfileSuggestParams, - UpdateUserProfileHook, } from './account_management'; export type { AuthenticationServiceStart, AuthenticationServiceSetup } from './authentication'; diff --git a/x-pack/plugins/security/public/mocks.ts b/x-pack/plugins/security/public/mocks.ts index f0081307ef33f..8a9232869b430 100644 --- a/x-pack/plugins/security/public/mocks.ts +++ b/x-pack/plugins/security/public/mocks.ts @@ -32,9 +32,6 @@ function createStartMock() { userProfile$: of({}), }, uiApi: getUiApiMock.createStart(), - hooks: { - useUpdateUserProfile: jest.fn(), - }, }; } diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx index 4a5e8ad545d64..13bcb3bcb4341 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx @@ -22,9 +22,8 @@ import type { Observable } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { UserAvatar } from '@kbn/user-profile-components'; +import { UserAvatar, type UserProfileAvatarData } from '@kbn/user-profile-components'; -import type { UserProfileAvatarData } from '../../common'; import { getUserDisplayName, isUserAnonymous } from '../../common/model'; import { useCurrentUser, useUserProfile } from '../components'; diff --git a/x-pack/plugins/security/public/plugin.test.tsx b/x-pack/plugins/security/public/plugin.test.tsx index 87ce15a19202d..fdab78a1f91d0 100644 --- a/x-pack/plugins/security/public/plugin.test.tsx +++ b/x-pack/plugins/security/public/plugin.test.tsx @@ -96,9 +96,6 @@ describe('Security Plugin', () => { "areAPIKeysEnabled": [Function], "getCurrentUser": [Function], }, - "hooks": Object { - "useUpdateUserProfile": [Function], - }, "navControlService": Object { "addUserMenuLinks": [Function], "getUserMenuLinks$": [Function], diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index e8c2c13ab0eb6..49c0a14e2fd9c 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -24,9 +24,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { SecurityLicense } from '../common/licensing'; import { SecurityLicenseService } from '../common/licensing'; -import type { UpdateUserProfileHook } from './account_management'; import { accountManagementApp, UserProfileAPIClient } from './account_management'; -import { getUseUpdateUserProfile } from './account_management/user_profile/use_update_user_profile'; import { AnalyticsService } from './analytics'; import { AnonymousAccessService } from './anonymous_access'; import type { AuthenticationServiceSetup, AuthenticationServiceStart } from './authentication'; @@ -213,12 +211,6 @@ export class SecurityPlugin ), userProfile$: this.securityApiClients.userProfiles.userProfile$, }, - hooks: { - useUpdateUserProfile: getUseUpdateUserProfile({ - apiClient: this.securityApiClients.userProfiles, - notifications: core.notifications, - }), - }, }; } @@ -263,13 +255,6 @@ export interface SecurityPluginStart { 'getCurrent' | 'bulkGet' | 'suggest' | 'update' | 'userProfile$' >; - /** - * A set of hooks to work with Kibana user profiles - */ - hooks: { - useUpdateUserProfile: UpdateUserProfileHook; - }; - /** * Exposes UI components that will be loaded asynchronously. * @deprecated diff --git a/x-pack/plugins/security_solution/common/api/tags/create_tag/create_tag_route.ts b/x-pack/plugins/security_solution/common/api/tags/create_tag/create_tag_route.ts new file mode 100644 index 0000000000000..160ae43b9aa0f --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/tags/create_tag/create_tag_route.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as rt from 'io-ts'; + +export const createTagRequest = rt.intersection([ + rt.type({ + name: rt.string, + description: rt.string, + }), + rt.partial({ color: rt.string }), +]); diff --git a/x-pack/plugins/security_solution/common/api/tags/get_dashboards_by_tags/get_dashboards_by_tags_route.ts b/x-pack/plugins/security_solution/common/api/tags/get_dashboards_by_tags/get_dashboards_by_tags_route.ts new file mode 100644 index 0000000000000..af2c89a967311 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/tags/get_dashboards_by_tags/get_dashboards_by_tags_route.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; + +export const getDashboardsRequest = rt.type({ tagIds: rt.array(rt.string) }); diff --git a/x-pack/plugins/security_solution/common/api/tags/get_tags_by_name/get_tags_by_name_route.ts b/x-pack/plugins/security_solution/common/api/tags/get_tags_by_name/get_tags_by_name_route.ts new file mode 100644 index 0000000000000..4ae16c320c02e --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/tags/get_tags_by_name/get_tags_by_name_route.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; + +export const getTagsByNameRequest = rt.type({ name: rt.string }); diff --git a/x-pack/plugins/security_solution/common/api/tags/index.ts b/x-pack/plugins/security_solution/common/api/tags/index.ts new file mode 100644 index 0000000000000..3f48011365f42 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/tags/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './create_tag/create_tag_route'; +export * from './get_dashboards_by_tags/get_dashboards_by_tags_route'; +export * from './get_tags_by_name/get_tags_by_name_route'; diff --git a/x-pack/plugins/security_solution/cypress/README.md b/x-pack/plugins/security_solution/cypress/README.md index db67eac909b19..6b3b641e8e59c 100644 --- a/x-pack/plugins/security_solution/cypress/README.md +++ b/x-pack/plugins/security_solution/cypress/README.md @@ -2,17 +2,12 @@ The `security_solution/cypress` directory contains functional UI tests that execute using [Cypress](https://www.cypress.io/). -Currently with Cypress you can develop `functional` tests and coming soon `CCS` and `Upgrade` functional tests. +Currently with Cypress you can develop `functional` tests. If you are still having doubts, questions or queries, please feel free to ping our Cypress champions: - Functional Tests: - Gloria Hornero and Patryk Kopycinsky -- CCS Tests: - - Technical questions around the https://github.com/elastic/integration-test repo: - - Domenico Andreoli - - Doubts regarding testing CCS and Cypress best practices: - - Gloria Hornero ## Table of Contents @@ -51,246 +46,17 @@ Run the tests with the following yarn scripts: | Script Name | Description | | ----------- | ----------- | | cypress | Runs the default Cypress command | -| cypress:open | Opens the Cypress UI with all tests in the `e2e` directory. This also runs a local kibana and ES instance. The kibana instance will reload when you make code changes. This is the recommended way to debug and develop tests. | -| cypress:open:ccs | Opens the Cypress UI and runs all tests in the `ccs_e2e` directory | -| cypress:open:upgrade | Opens the Cypress UI and runs all tests in the `upgrade_e2e` directory | +| cypress:open | Opens the Cypress UI with all tests in the `e2e` directory. This also runs a local kibana and ES instance. The kibana instance will reload when you make code changes. This is the recommended way to debug and develop tests. |C | cypress:run | Runs all tests in the `e2e` directory excluding `investigations` and `explore` directories in headless mode | | cypress:run:cases | Runs all tests under `explore/cases` in the `e2e` directory related to the Cases area team in headless mode | | cypress:run:reporter | Runs all tests with the specified configuration in headless mode and produces a report using `cypress-multi-reporters` | | cypress:run:respops | Runs all tests related to the Response Ops area team, specifically tests in `detection_alerts`, `detection_rules`, and `exceptions` directories in headless mode | -| cypress:run:ccs | Runs all tests in the `ccs_e2e` directory in headless mode | -| cypress:run:upgrade | Runs all tests in the `upgrade_e2e` directory in headless mode | | cypress:investigations:run | Runs all tests in the `e2e/investigations` directory in headless mode | | cypress:explore:run | Runs all tests in the `e2e/explore` directory in headless mode | | junit:merge | Merges individual test reports into a single report and moves the report to the `junit` directory | Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging. -### Execution modes - -There are currently four ways to run the tests, comprised of two execution modes and two target environments, which will be detailed below. - -#### Interactive mode - -When you run Cypress in interactive mode, an interactive runner is displayed that allows you to see commands as they execute while also viewing the application under test. For more information, please see [cypress documentation](https://docs.cypress.io/guides/core-concepts/test-runner.html#Overview). - -#### Headless mode - -A headless browser is a browser simulation program that does not have a user interface. These programs operate like any other browser, but do not display any UI. This is why meanwhile you are executing the tests on this mode you are not going to see the application under test. Just the output of the test is displayed on the terminal once the execution is finished. - -### Target environments - -#### FTR (CI) - -This is the configuration used by CI. It uses the FTR to spawn both a Kibana instance (http://localhost:5620) and an Elasticsearch instance (http://localhost:9220) with a preloaded minimum set of data (see preceding "Test data" section), and then executes cypress against this stack. You can find this configuration in `x-pack/test/security_solution_cypress` - -Tests run on buildkite PR pipeline is parallelized. It can be configured in [.buildkite/pipelines/pull_request/security_solution.yml](https://github.com/elastic/kibana/blob/main/.buildkite/pipelines/pull_request/security_solution.yml) with property `parallelism` - -```yml - ... - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - parallelism: 4 - ... -``` - -#### Custom Targets - -This configuration runs cypress tests against an arbitrary host. -**WARNING**: When using your own instances you need to take into account that if you already have data on it, the tests may fail, as well as, they can put your instances in an undesired state, since our tests uses es_archive to populate data. - -#### integration-test (CI) - -This configuration is driven by [elastic/integration-test](https://github.com/elastic/integration-test) which, as part of a bigger set of tests, provisions one VM with two instances configured in CCS mode and runs the [CCS Cypress test specs](./ccs_e2e). - -The two clusters are named `admin` and `data` and are reachable as follows: - -| | Elasticsearch | Kibana | -| ----- | ---------------------- | ---------------------- | -| admin | https://localhost:9200 | https://localhost:5601 | -| data | https://localhost:9210 | https://localhost:5602 | - -### Working with integration-test - -#### Initial setup and prerequisites - -The entry point is [integration-test/jenkins_test.sh](https://github.com/elastic/integration-test/blob/master/jenkins_test.sh), it essentially prepares the VMs and there runs tests. Some snapshots (`phase1` and `phase2`) are taken along the way so that it's possible to short cut the VM preparation when iterating over tests for development or debugging. - -The VMs are managed with Vagrant using the VirtualBox provider therefore you need to install both these tools. The host OS can be either Windows, Linux or MacOS. - -`jenkins_test.sh` assumes that a `kibana` folder is present alongside the `integration-test` where it's executed from. The `kibana` folder is used only for loading the test suites though, the actual packages for the VMs preparation are downloaded from elastic.co according to the `BUILD` environment variable or the branch which `jenkins_test.sh` is invoked from. It's your responsibility to checkout the matching branches in `kibana` and `integration-test` as needed. - -Read [integration-test#readme](https://github.com/elastic/integration-test#readme) for further details. - -#### Use cases - -There is no way to just set up the test environment without also executing tests at least once. On the other hand it's time consuming to go throught the whole CI procedure to just iterate over the tests therefore the following instructions support the two use cases: - -- reproduce e2e the CI execution locally, ie. for debugging a CI failure -- use the CI script to easily setup the environment for tests development/debugging - -The missing use case, application TDD, requires a different solution that runs from the checked out repositories instead of the pre-built packages and it's yet to be developed. - -#### Run the CI flow - -This is the CI flow narrowed down to the execution of CCS Cypress tests: - -```shell -cd integration-test -VMS=ubuntu16_tar_ccs_cypress ./jenkins_test.sh -``` - -It destroys and rebuilds the VM. There installs, provisions and starts the stack according to the configuration in [integration-test/provision/ubuntu16_tar_ccs_cypress.sh](https://github.com/elastic/integration-test/blob/master/provision/ubuntu16_tar_ccs_cypress.sh). - -The tests are executed using the FTR runner `SecuritySolutionCypressCcsTestRunner` defined in [x-pack/test/security_solution_cypress/runner.ts](../../../test/security_solution_cypress/runner.ts) as configured in [x-pack/test/security_solution_cypress/ccs_config.ts](../../../test/security_solution_cypress/ccs_config.ts). - -#### Re-run the tests - -After the first run it's possible to restore the VM at `phase2`, right before tests were executed, and run them again: - -```shell -cd integration-test -MODE=retest ./jenkins_test.sh -``` - -It remembers which VM the first round was executed on, you don't need to specify `VMS` any more. - -In case your tests are cleaning after themselves and therefore result idempotent, you can skip the restoration to `phase2` and directly run the Cypress command line. See [CCS Custom Target + Headless](#ccs-custom-target--headless) further below for details but ensure you'll define the `CYPRESS_*` following the correspondence: - -| Cypress command line | [integration-test/provision/ubuntu16_tar_ccs_cypress.sh](https://github.com/elastic/integration-test/blob/master/provision/ubuntu16_tar_ccs_cypress.sh) | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| CYPRESS_BASE_URL | TEST_KIBANA_URL | -| CYPRESS_ELASTICSEARCH_URL | TEST_ES_URL | -| CYPRESS_CCS_KIBANA_URL | TEST_KIBANA_URLDATA | -| CYPRESS_CCS_ELASTICSEARCH_URL | TEST_ES_URLDATA | -| CYPRESS_CCS_REMOTE_NAME | TEST_CCS_REMOTE_NAME | -| CYPRESS_ELASTICSEARCH_USERNAME | ELASTICSEARCH_USERNAME | -| CYPRESS_ELASTICSEARCH_PASSWORD | ELASTICSEARCH_PASSWORD | -| TEST_CA_CERT_PATH | integration-test/certs/ca/ca.crt | - -Note: `TEST_CA_CERT_PATH` above is truly without `CYPRESS_` prefix. - -### Test Execution: Examples - -#### FTR + Headless (Chrome) - -Since this is how tests are run on CI, this will likely be the configuration you want to reproduce failures locally, etc. - -```shell -# bootstrap kibana from the project root -yarn kbn bootstrap - -# build the plugins/assets that cypress will execute against -node scripts/build_kibana_platform_plugins - -# launch the cypress test runner -cd x-pack/plugins/security_solution -yarn cypress:run-as-ci -``` - -#### FTR + Headless (Firefox) - -Since this is how tests are run on CI, this will likely be the configuration you want to reproduce failures locally, etc. - -```shell -# bootstrap kibana from the project root -yarn kbn bootstrap - -# build the plugins/assets that cypress will execute against -node scripts/build_kibana_platform_plugins - -# launch the cypress test runner -cd x-pack/plugins/security_solution -yarn cypress:run-as-ci:firefox -``` - -#### FTR + Interactive - -This is the preferred mode for developing new tests. - -```shell -# bootstrap kibana from the project root -yarn kbn bootstrap - -# build the plugins/assets that cypress will execute against -node scripts/build_kibana_platform_plugins - -# launch the cypress test runner -cd x-pack/plugins/security_solution -yarn cypress:open-as-ci -``` - -Note that you can select the browser you want to use on the top right side of the interactive runner. - -#### Custom Target + Headless (Chrome) - -This mode may be useful for testing a release, e.g. spin up a build candidate -and point cypress at it to catch regressions. - -```shell -# bootstrap kibana from the project root -yarn kbn bootstrap - -# load auditbeat data needed for test execution (which FTR normally does for us) -cd x-pack/plugins/security_solution -node ../../../scripts/es_archiver load auditbeat --dir ../../test/security_solution_cypress/es_archives --config ../../../test/functional/config.base.js --es-url http(s)://:@ --kibana-url http(s)://:@ - -# launch the cypress test runner with overridden environment variables -cd x-pack/plugins/security_solution -CYPRESS_BASE_URL=http(s)://:@ CYPRESS_ELASTICSEARCH_URL=http(s)://:@ CYPRESS_ELASTICSEARCH_USERNAME= CYPRESS_ELASTICSEARCH_PASSWORD= yarn cypress:run -``` - -#### Custom Target + Headless (Firefox) - -This mode may be useful for testing a release, e.g. spin up a build candidate -and point cypress at it to catch regressions. - -```shell -# bootstrap kibana from the project root -yarn kbn bootstrap - -# load auditbeat data needed for test execution (which FTR normally does for us) -cd x-pack/plugins/security_solution -node ../../../scripts/es_archiver load auditbeat --dir ../../test/security_solution_cypress/es_archives --config ../../../test/functional/config.base.js --es-url http(s)://:@ --kibana-url http(s)://:@ - -# launch the cypress test runner with overridden environment variables -cd x-pack/plugins/security_solution -CYPRESS_BASE_URL=http(s)://:@ CYPRESS_ELASTICSEARCH_URL=http(s)://:@ CYPRESS_ELASTICSEARCH_USERNAME= CYPRESS_ELASTICSEARCH_PASSWORD= yarn cypress:run:firefox -``` - -#### CCS Custom Target + Headless - -This test execution requires two clusters configured for CCS. See [Search across clusters](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-cross-cluster-search.html) for instructions on how to prepare such setup. - -The instructions below assume: - -- Search cluster is on server1 -- Remote cluster is on server2 -- Remote cluster is accessible from the search cluster with name `remote` -- Security and TLS are enabled - -```shell -# bootstrap Kibana from the project root -yarn kbn bootstrap - -# launch the Cypress test runner with overridden environment variables -cd x-pack/plugins/security_solution -CYPRESS_ELASTICSEARCH_USERNAME="user" \ -CYPRESS_ELASTICSEARCH_PASSWORD="pass" \ -CYPRESS_BASE_URL="https://user:pass@server1:5601" \ -CYPRESS_ELASTICSEARCH_URL="https://user:pass@server1:9200" \ -CYPRESS_CCS_KIBANA_URL="https://user:pass@server2:5601" \ -CYPRESS_CCS_ELASTICSEARCH_URL="https://user:pass@server2:9200" \ -CYPRESS_CCS_REMOTE_NAME="remote" \ -yarn cypress:run:ccs -``` - -Similar sequence, just ending with `yarn cypress:open:ccs`, can be used for interactive test running via Cypress UI. - -Appending `--browser firefox` to the `yarn cypress:run:ccs` command above will run the tests on Firefox instead of Chrome. - ## Debugging your test In order to be able to debug any Cypress test you need to open Cypress on visual mode. [Here](https://docs.cypress.io/guides/guides/debugging) @@ -302,10 +68,6 @@ If you are debugging a flaky test, a good tip is to insert a `cy.wait('); -``` - -They will use the `CYPRESS_CCS_*_URL` environment variables for accessing the remote cluster. Complex tests involving local and remote data can interleave them with `esArchiverLoad` and `esArchiverUnload` as needed. - -#### Remote indices queries - -Queries accessing remote indices follow the usual `:` notation but should not hard-code the remote name in the test itself. - -For such reason the environemnt variable `CYPRESS_CCS_REMOTE_NAME` is defined and, in the case of detection rules, used as shown below: - -```javascript -const ccsRemoteName: string = Cypress.env('CCS_REMOTE_NAME'); - -export const unmappedCCSRule: CustomRule = { - customQuery: '*:*', - index: [`${ccsRemoteName}:unmapped*`], - ... -}; - -``` - -Similar approach should be used in defining all index patterns, rules, and queries to be applied on remote data. - ## Development Best Practices Below you will a set of best practices that should be followed when writing Cypress tests. @@ -478,12 +202,6 @@ taken into consideration until another solution is implemented: Remember that minimizing the number of times the web page is loaded, we minimize as well the execution time. -### CCS test specific - -When testing CCS we want to put our focus in making sure that our `Source` instance is receiving properly the data that comes from the `Remote` instances, as well as the data is displayed as we expect on the `Source`. - -For that reason and in order to make our test more stable, use the API to execute all the actions needed before the assertions, and use Cypress to assert that the UI is displaying all the expected things. - ## Test Artifacts When Cypress tests are run headless on the command line, artifacts diff --git a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_alerts/alerts_details.cy.ts b/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_alerts/alerts_details.cy.ts deleted file mode 100644 index 2865eb6e5ee9b..0000000000000 --- a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_alerts/alerts_details.cy.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { JSON_TEXT } from '../../screens/alerts_details'; - -import { expandFirstAlert, waitForAlerts } from '../../tasks/alerts'; -import { openJsonView } from '../../tasks/alerts_details'; -import { createRule } from '../../tasks/api_calls/rules'; -import { cleanKibana } from '../../tasks/common'; -import { login, visitWithoutDateRange } from '../../tasks/login'; - -import { getUnmappedCCSRule } from '../../objects/rule'; - -import { ALERTS_URL } from '../../urls/navigation'; - -describe('Alert details with unmapped fields', () => { - beforeEach(() => { - login(); - cleanKibana(); - cy.task('esArchiverCCSLoad', 'unmapped_fields'); - createRule(getUnmappedCCSRule()); - visitWithoutDateRange(ALERTS_URL); - waitForAlerts(); - expandFirstAlert(); - }); - - it('Displays the unmapped field on the JSON view', () => { - const expectedUnmappedValue = 'This is the unmapped field'; - - openJsonView(); - - cy.get(JSON_TEXT).then((x) => { - const parsed = JSON.parse(x.text()); - expect(parsed.fields.unmapped[0]).to.equal(expectedUnmappedValue); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_rules/event_correlation_rule.cy.ts b/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_rules/event_correlation_rule.cy.ts deleted file mode 100644 index ea2a1797fec5b..0000000000000 --- a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_rules/event_correlation_rule.cy.ts +++ /dev/null @@ -1,53 +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 { getCCSEqlRule } from '../../objects/rule'; - -import { ALERTS_COUNT, ALERT_DATA_GRID } from '../../screens/alerts'; - -import { - filterByCustomRules, - goToRuleDetails, - waitForRulesTableToBeLoaded, -} from '../../tasks/alerts_detection_rules'; -import { createRule } from '../../tasks/api_calls/rules'; -import { cleanKibana } from '../../tasks/common'; -import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule'; -import { login, visitWithoutDateRange } from '../../tasks/login'; - -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; - -describe('Detection rules', function () { - const expectedNumberOfAlerts = '1 alert'; - - beforeEach('Reset signals index', function () { - cleanKibana(); - }); - - it('EQL rule on remote indices generates alerts', function () { - cy.task('esArchiverCCSLoad', 'linux_process'); - const rule = getCCSEqlRule(); - login(); - createRule(rule); - visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToBeLoaded(); - filterByCustomRules(); - goToRuleDetails(); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts); - cy.get(ALERT_DATA_GRID) - .invoke('text') - .then((text) => { - cy.log('ALERT_DATA_GRID', text); - expect(text).contains(rule.name); - expect(text).contains(rule.severity); - expect(text).contains(rule.risk_score); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts index 599243ae37d29..3cfe58f22893c 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts @@ -70,7 +70,7 @@ describe( ); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_DOC_COUNT_CELL).should( 'contain.text', - 2 + 0 ); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_HOST_PREVALENCE_CELL).should( 'contain.text', diff --git a/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts index 44a2326aebc8e..11fc62c7f68f7 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts @@ -111,12 +111,12 @@ describe( expandDocumentDetailsExpandableFlyoutLeftSection(); cy.get(DOCUMENT_DETAILS_FLYOUT_COLLAPSE_DETAILS_BUTTON) .should('be.visible') - .and('have.text', 'Collapse alert details'); + .and('have.text', 'Collapse details'); collapseDocumentDetailsExpandableFlyoutLeftSection(); cy.get(DOCUMENT_DETAILS_FLYOUT_EXPAND_DETAILS_BUTTON) .should('be.visible') - .and('have.text', 'Expand alert details'); + .and('have.text', 'Expand details'); cy.log('Verify the take action button is visible on all tabs'); diff --git a/x-pack/plugins/security_solution/cypress/support/e2e.js b/x-pack/plugins/security_solution/cypress/support/e2e.js index 3984b4be49727..477c2606153b7 100644 --- a/x-pack/plugins/security_solution/cypress/support/e2e.js +++ b/x-pack/plugins/security_solution/cypress/support/e2e.js @@ -23,6 +23,9 @@ // Import commands.js using ES2015 syntax: import './commands'; import 'cypress-real-events/support'; +import registerCypressGrep from '@cypress/grep'; + +registerCypressGrep(); Cypress.on('uncaught:exception', () => { return false; diff --git a/x-pack/plugins/security_solution/cypress/upgrade_e2e/detections/detection_rules/custom_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/upgrade_e2e/detections/detection_rules/custom_query_rule.cy.ts deleted file mode 100644 index a9950f840fa72..0000000000000 --- a/x-pack/plugins/security_solution/cypress/upgrade_e2e/detections/detection_rules/custom_query_rule.cy.ts +++ /dev/null @@ -1,141 +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 semver from 'semver'; -import { - ALERT_GRID_CELL, - DESTINATION_IP, - HOST_NAME, - PROCESS_NAME_COLUMN, - PROCESS_NAME, - REASON, - RISK_SCORE, - RULE_NAME, - SEVERITY, - SOURCE_IP, - USER_NAME, -} from '../../../screens/alerts'; -import { SERVER_SIDE_EVENT_COUNT } from '../../../screens/alerts_detection_rules'; -import { - ADDITIONAL_LOOK_BACK_DETAILS, - ABOUT_DETAILS, - ABOUT_RULE_DESCRIPTION, - CUSTOM_QUERY_DETAILS, - DEFINITION_DETAILS, - INDEX_PATTERNS_DETAILS, - RISK_SCORE_DETAILS, - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - TIMELINE_TEMPLATE_DETAILS, -} from '../../../screens/rule_details'; - -import { getDetails } from '../../../tasks/rule_details'; -import { waitForPageToBeLoaded } from '../../../tasks/common'; -import { - waitForRulesTableToBeLoaded, - goToTheRuleDetailsOf, -} from '../../../tasks/alerts_detection_rules'; -import { login, visit } from '../../../tasks/login'; - -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; - -const EXPECTED_NUMBER_OF_ALERTS = '1'; - -const alert = { - rule: 'Custom query rule for upgrade', - severity: 'low', - riskScore: '7', - reason: - 'file event with process test, file The file to test, by Security Solution on security-solution.local created low alert Custom query rule for upgrade.', - reasonAlt: '—', - hostName: 'security-solution.local', - username: 'Security Solution', - processName: 'test', - fileName: 'The file to test', - sourceIp: '127.0.0.1', - destinationIp: '127.0.0.2', -}; - -const rule = { - customQuery: '*:*', - name: 'Custom query rule for upgrade', - description: 'My description', - index: ['auditbeat-custom*'], - severity: 'Low', - riskScore: '7', - timelineTemplate: 'none', - runsEvery: '24h', - lookBack: '49976h', - timeline: 'None', -}; - -describe('After an upgrade, the custom query rule', () => { - before(() => { - login(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToBeLoaded(); - goToTheRuleDetailsOf(rule.name); - waitForPageToBeLoaded(); - // Possible bug on first attempt sometimes redirects page back to alerts - // Going to retry the block once - cy.url().then((url) => { - const currentUrl = url; - cy.log(`Current URL is : ${currentUrl}`); - if (!currentUrl.includes(DETECTIONS_RULE_MANAGEMENT_URL)) { - cy.log('Retrying not on correct page!'); - visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToBeLoaded(); - goToTheRuleDetailsOf(rule.name); - waitForPageToBeLoaded(); - } - }); - cy.url().should('include', DETECTIONS_RULE_MANAGEMENT_URL); - }); - - it('Has the expected alerts number', () => { - cy.get(SERVER_SIDE_EVENT_COUNT).contains(EXPECTED_NUMBER_OF_ALERTS); - }); - - it('Displays the rule details', () => { - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore); - }); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.customQuery); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', rule.timeline); - }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should('have.text', rule.runsEvery); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', rule.lookBack); - }); - }); - - it('Displays the alert details at the tgrid', () => { - let expectedReason = alert.reason; - if (semver.lt(Cypress.env('ORIGINAL_VERSION'), '7.15.0')) { - expectedReason = alert.reasonAlt; - } - cy.get(ALERT_GRID_CELL).first().focus(); - cy.get(RULE_NAME).should('have.text', alert.rule); - cy.get(SEVERITY).should('have.text', alert.severity); - cy.get(RISK_SCORE).should('have.text', alert.riskScore); - cy.get(REASON).contains(expectedReason); - cy.get(HOST_NAME).should('have.text', alert.hostName); - cy.get(USER_NAME).should('have.text', alert.username); - cy.get(PROCESS_NAME_COLUMN).eq(0).scrollIntoView(); - cy.get(PROCESS_NAME).should('have.text', alert.processName); - cy.get(SOURCE_IP).should('have.text', alert.sourceIp); - cy.get(DESTINATION_IP).should('have.text', alert.destinationIp); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/upgrade_e2e/detections/detection_rules/threshold_rule.cy.ts b/x-pack/plugins/security_solution/cypress/upgrade_e2e/detections/detection_rules/threshold_rule.cy.ts deleted file mode 100644 index 78c34dfd43a7e..0000000000000 --- a/x-pack/plugins/security_solution/cypress/upgrade_e2e/detections/detection_rules/threshold_rule.cy.ts +++ /dev/null @@ -1,138 +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 semver from 'semver'; -import { REASON, RISK_SCORE, RULE_NAME, SEVERITY } from '../../../screens/alerts'; -import { SERVER_SIDE_EVENT_COUNT } from '../../../screens/alerts_detection_rules'; -import { - ADDITIONAL_LOOK_BACK_DETAILS, - ABOUT_DETAILS, - ABOUT_RULE_DESCRIPTION, - CUSTOM_QUERY_DETAILS, - DEFINITION_DETAILS, - INDEX_PATTERNS_DETAILS, - RISK_SCORE_DETAILS, - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - THRESHOLD_DETAILS, - TIMELINE_TEMPLATE_DETAILS, -} from '../../../screens/rule_details'; - -import { getDetails } from '../../../tasks/rule_details'; -import { expandFirstAlert } from '../../../tasks/alerts'; -import { waitForPageToBeLoaded } from '../../../tasks/common'; -import { - goToTheRuleDetailsOf, - waitForRulesTableToBeLoaded, -} from '../../../tasks/alerts_detection_rules'; -import { login, visit } from '../../../tasks/login'; - -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; -import { - OVERVIEW_RISK_SCORE, - OVERVIEW_RULE, - OVERVIEW_SEVERITY, - OVERVIEW_STATUS, - OVERVIEW_RULE_TYPE, -} from '../../../screens/alerts_details'; - -const EXPECTED_NUMBER_OF_ALERTS = '1'; - -const alert = { - rule: 'Threshold rule', - severity: 'medium', - riskScore: '17', - reason: 'event created medium alert Threshold rule.', - reasonAlt: '—', - hostName: 'security-solution.local', - thresholdCount: '2', -}; - -const rule = { - customQuery: '*:*', - name: 'Threshold rule', - description: 'Threshold rule for testing upgrade', - index: ['auditbeat-threshold*'], - severity: 'Medium', - riskScore: '17', - timelineTemplate: 'none', - runsEvery: '24h', - lookBack: '49976h', - timeline: 'None', - ruleType: 'threshold', - thresholdField: 'host.name', - thresholdValue: '1', -}; - -describe('After an upgrade, the threshold rule', () => { - before(() => { - login(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToBeLoaded(); - goToTheRuleDetailsOf(rule.name); - waitForPageToBeLoaded(); - }); - - it('Has the expected alerts number', () => { - cy.get(SERVER_SIDE_EVENT_COUNT).contains(EXPECTED_NUMBER_OF_ALERTS); - }); - - it('Displays the rule details', () => { - cy.get(RULE_NAME_HEADER).should('contain', rule.name); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore); - }); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.customQuery); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Threshold'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', rule.timeline); - getDetails(THRESHOLD_DETAILS).should( - 'have.text', - `Results aggregated by ${rule.thresholdField} >= ${rule.thresholdValue}` - ); - }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should('have.text', rule.runsEvery); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', rule.lookBack); - }); - }); - - it('Displays the alert details in the TGrid', () => { - let expectedReason = alert.reason; - if (semver.lt(Cypress.env('ORIGINAL_VERSION'), '7.15.0')) { - expectedReason = alert.reasonAlt; - } - cy.scrollTo('bottom'); - cy.get(RULE_NAME).should('have.text', alert.rule); - cy.get(SEVERITY).should('have.text', alert.severity); - cy.get(RISK_SCORE).should('have.text', alert.riskScore); - cy.get(REASON).contains(expectedReason); - // TODO: Needs data-test-subj - // cy.get(HOST_NAME).should('have.text', alert.hostName); - }); - - it('Displays the Overview alert details in the alert flyout', () => { - expandFirstAlert(); - - cy.get(OVERVIEW_STATUS).should('have.text', 'open'); - cy.get(OVERVIEW_RULE).should('have.text', alert.rule); - cy.get(OVERVIEW_SEVERITY).contains(alert.severity, { matchCase: false }); - cy.get(OVERVIEW_RISK_SCORE).should('have.text', alert.riskScore); - // TODO: Find out what this is - // cy.get(OVERVIEW_HOST_NAME).should('have.text', alert.hostName); - // TODO: Needs data-test-subj - // cy.get(OVERVIEW_THRESHOLD_COUNT).should('have.text', alert.thresholdCount); - cy.get(OVERVIEW_RULE_TYPE).should('have.text', rule.ruleType); - // TODO: Needs data-test-subj - // cy.get(OVERVIEW_THRESHOLD_VALUE).should('have.text', rule.thresholdValue); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/upgrade_e2e/threat_hunting/cases/import_case.cy.ts b/x-pack/plugins/security_solution/cypress/upgrade_e2e/threat_hunting/cases/import_case.cy.ts deleted file mode 100644 index 4eecc36fe928a..0000000000000 --- a/x-pack/plugins/security_solution/cypress/upgrade_e2e/threat_hunting/cases/import_case.cy.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ALL_CASES_CLOSED_CASES_STATS, - ALL_CASES_COMMENTS_COUNT, - ALL_CASES_IN_PROGRESS_CASES_STATS, - ALL_CASES_NAME, - ALL_CASES_NOT_PUSHED, - ALL_CASES_NUMBER_OF_ALERTS, - ALL_CASES_OPEN_CASES_STATS, - ALL_CASES_IN_PROGRESS_STATUS, -} from '../../../screens/all_cases'; -import { - CASES_TAGS, - CASE_CONNECTOR, - CASE_DETAILS_PAGE_TITLE, - CASE_DETAILS_USERNAMES, - CASE_EVENT_UPDATE, - CASE_IN_PROGRESS_STATUS, - CASE_SWITCH, - CASE_USER_ACTION, -} from '../../../screens/case_details'; -import { CASES_PAGE } from '../../../screens/kibana_navigation'; - -import { goToCaseDetails } from '../../../tasks/all_cases'; -import { deleteCase } from '../../../tasks/case_details'; -import { - navigateFromKibanaCollapsibleTo, - openKibanaNavigation, -} from '../../../tasks/kibana_navigation'; -import { login, visitWithoutDateRange } from '../../../tasks/login'; -import { importCase } from '../../../tasks/saved_objects'; - -import { KIBANA_SAVED_OBJECTS } from '../../../urls/navigation'; - -const CASE_NDJSON = '7_16_case.ndjson'; -const importedCase = { - title: '7.16 case to export', - user: 'glo', - initial: 'g', - reporter: 'glo@test.co', - tags: 'export case', - numberOfAlerts: '1', - numberOfComments: '2', - description: - "This is the description of the 7.16 case that I'm going to import in future versions.", - timeline: 'This is just a timeline', - status: 'In progress', - ruleName: 'This is a test', - participants: ['test', 'elastic'], - connector: 'Jira test', -}; -const updateStatusRegex = new RegExp( - `\\S${importedCase.user}marked case as${importedCase.status}\\S*\\s?(\\S*)?\\s?(\\S*)?` -); -const alertUpdateRegex = new RegExp( - `\\S${importedCase.user}added an alert from Unknown\\S*\\s?(\\S*)?\\s?(\\S*)?` -); -const incidentManagementSystemRegex = new RegExp( - `\\S${importedCase.participants[0]}selected ${importedCase.connector} as incident management system\\S*\\s?(\\S*)?\\s?(\\S*)?` -); -const DESCRIPTION = 0; -const TIMELINE = 1; -const LENS = 2; -const STATUS_UPDATE = 0; -const FIRST_ALERT_UPDATE = 1; -const SECOND_ALERT_UPDATE = 2; -const INCIDENT_MANAGEMENT_SYSTEM_UPDATE = 3; -const EXPECTED_NUMBER_OF_UPDATES = 4; -const REPORTER = 0; - -describe('Import case after upgrade', () => { - before(() => { - login(); - visitWithoutDateRange(KIBANA_SAVED_OBJECTS); - importCase(CASE_NDJSON); - openKibanaNavigation(); - navigateFromKibanaCollapsibleTo(CASES_PAGE); - }); - - after(() => { - deleteCase(); - }); - - it('Displays the correct number of opened cases on the cases page', () => { - const EXPECTED_NUMBER_OF_OPENED_CASES = '0'; - cy.get(ALL_CASES_OPEN_CASES_STATS).should('have.text', EXPECTED_NUMBER_OF_OPENED_CASES); - }); - - it('Displays the correct number of in progress cases on the cases page', () => { - const EXPECTED_NUMBER_OF_IN_PROGRESS_CASES = '1'; - cy.get(ALL_CASES_IN_PROGRESS_CASES_STATS).should( - 'have.text', - EXPECTED_NUMBER_OF_IN_PROGRESS_CASES - ); - }); - - it('Displays the correct number of closed cases on the cases page', () => { - const EXPECTED_NUMBER_OF_CLOSED_CASES = '0'; - cy.get(ALL_CASES_CLOSED_CASES_STATS).should('have.text', EXPECTED_NUMBER_OF_CLOSED_CASES); - }); - - it('Displays the correct case details on the cases page', () => { - cy.get(ALL_CASES_NAME).should('have.text', importedCase.title); - cy.get(ALL_CASES_NUMBER_OF_ALERTS).should('have.text', importedCase.numberOfAlerts); - cy.get(ALL_CASES_COMMENTS_COUNT).should('have.text', importedCase.numberOfComments); - cy.get(ALL_CASES_NOT_PUSHED).should('be.visible'); - cy.get(ALL_CASES_IN_PROGRESS_STATUS).should('be.visible'); - }); - - it('Displays the correct case details on the case details page', () => { - goToCaseDetails(); - - cy.get(CASE_DETAILS_PAGE_TITLE).should('have.text', importedCase.title); - cy.get(CASE_IN_PROGRESS_STATUS).should('exist'); - cy.get(CASE_SWITCH).should('have.attr', 'aria-checked', 'false'); - cy.get(CASE_USER_ACTION).eq(DESCRIPTION).should('have.text', importedCase.description); - cy.get(CASE_USER_ACTION).eq(TIMELINE).should('have.text', importedCase.timeline); - cy.get(CASE_USER_ACTION).eq(LENS).should('have.text', ''); - cy.get(CASE_EVENT_UPDATE).should('have.length', EXPECTED_NUMBER_OF_UPDATES); - cy.get(CASE_EVENT_UPDATE).eq(STATUS_UPDATE).invoke('text').should('match', updateStatusRegex); - cy.get(CASE_EVENT_UPDATE) - .eq(FIRST_ALERT_UPDATE) - .invoke('text') - .should('match', alertUpdateRegex); - cy.get(CASE_EVENT_UPDATE) - .eq(SECOND_ALERT_UPDATE) - .invoke('text') - .should('match', alertUpdateRegex); - cy.get(CASE_EVENT_UPDATE) - .eq(INCIDENT_MANAGEMENT_SYSTEM_UPDATE) - .invoke('text') - .should('match', incidentManagementSystemRegex); - // TODO: Needs data-test-subj - // cy.get(CASE_DETAILS_USERNAMES).should('have.length', EXPECTED_NUMBER_OF_PARTICIPANTS); - // TODO: Investigate why this changes, not reliable to verify - // cy.get(CASE_DETAILS_USERNAMES).eq(FIRST_PARTICIPANT).should('have.text', importedCase.user); - // cy.get(CASE_DETAILS_USERNAMES) - // .eq(SECOND_PARTICIPANT) - // .should('have.text', importedCase.participants[0]); - // cy.get(CASE_DETAILS_USERNAMES) - // .eq(THIRD_PARTICIPANT) - // .should('have.text', importedCase.participants[1]); - cy.get(CASE_DETAILS_USERNAMES).eq(REPORTER).should('have.text', importedCase.user); - cy.get(CASES_TAGS(importedCase.tags)).should('exist'); - cy.get(CASE_CONNECTOR).should('have.text', importedCase.connector); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/upgrade_e2e/threat_hunting/timeline/import_timeline.cy.ts b/x-pack/plugins/security_solution/cypress/upgrade_e2e/threat_hunting/timeline/import_timeline.cy.ts deleted file mode 100644 index 476ad34652a63..0000000000000 --- a/x-pack/plugins/security_solution/cypress/upgrade_e2e/threat_hunting/timeline/import_timeline.cy.ts +++ /dev/null @@ -1,201 +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 semver from 'semver'; -import { - CORRELATION_EVENT_TABLE_CELL, - DATA_PROVIDERS, - DATE_PICKER_END, - DATE_PICKER_START, - DESTINATION_IP_KPI, - GRAPH_TAB_BUTTON, - HOST_KPI, - QUERY_TAB_BUTTON, - NOTE_DESCRIPTION, - NOTE_PREVIEW, - NOTES_TAB_BUTTON, - PINNED_TAB_BUTTON, - PROCESS_KPI, - QUERY_EVENT_TABLE_CELL, - SOURCE_IP_KPI, - TIMELINE_CORRELATION_TAB, - TIMELINE_CORRELATION_INPUT, - TIMELINE_DESCRIPTION, - TIMELINE_QUERY, - TIMELINE_TITLE, - USER_KPI, -} from '../../../screens/timeline'; -import { - NOTE, - TIMELINES_USERNAME, - TIMELINE_NAME, - TIMELINES_DESCRIPTION, - TIMELINES_NOTES_COUNT, - TIMELINES_PINNED_EVENT_COUNT, -} from '../../../screens/timelines'; - -import { login, visitWithoutDateRange } from '../../../tasks/login'; -import { - closeTimeline, - deleteTimeline, - goToCorrelationTab, - goToNotesTab, - setKibanaTimezoneToUTC, -} from '../../../tasks/timeline'; -import { expandNotes, importTimeline, openTimeline } from '../../../tasks/timelines'; - -import { TIMELINES_URL } from '../../../urls/navigation'; - -const timeline = '7_15_timeline.ndjson'; -const username = 'elastic'; - -const timelineDetails = { - dateStart: 'Oct 10, 2020 @ 22:00:00.000', - dateEnd: 'Oct 11, 2030 @ 15:13:15.851', - queryTab: 'Query4', - queryTabAlt: 'Query2', - correlationTab: 'Correlation', - analyzerTab: 'Analyzer', - notesTab: 'Notes2', - pinnedTab: 'Pinned1', -}; - -const detectionAlert = { - message: '—', - eventCategory: 'file', - eventAction: 'initial_scan', - hostName: 'security-solution.local', - sourceIp: '127.0.0.1', - destinationIp: '127.0.0.2', - userName: 'Security Solution', -}; - -const event = { - timestamp: 'Nov 4, 2021 @ 10:09:29.438', - message: '—', - eventCategory: 'file', - eventAction: 'initial_scan', - hostName: 'security-solution.local', - sourceIp: '127.0.0.1', - destinationIp: '127.0.0.2', - userName: 'Security Solution', -}; - -describe('Import timeline after upgrade', () => { - before(() => { - login(); - visitWithoutDateRange(TIMELINES_URL); - importTimeline(timeline); - setKibanaTimezoneToUTC(); - }); - - after(() => { - closeTimeline(); - deleteTimeline(); - }); - - it('Displays the correct timeline details on the timelines page', () => { - cy.readFile(`cypress/fixtures/${timeline}`).then((file) => { - const timelineJson = JSON.parse(file); - const regex = new RegExp( - `\\S${timelineJson.globalNotes[0].createdBy}added a note\\S*\\s?(\\S*)?\\s?(\\S*)?${timelineJson.globalNotes[0].createdBy} added a note${timelineJson.globalNotes[0].note}` - ); - - cy.get(TIMELINE_NAME).should('have.text', timelineJson.title); - cy.get(TIMELINES_DESCRIPTION).should('have.text', timelineJson.description); - cy.get(TIMELINES_USERNAME).should('have.text', username); - cy.get(TIMELINES_NOTES_COUNT).should('have.text', timelineJson.globalNotes.length.toString()); - cy.get(TIMELINES_PINNED_EVENT_COUNT).should( - 'have.text', - timelineJson.pinnedEventIds.length.toString() - ); - - expandNotes(); - - cy.get(NOTE).invoke('text').should('match', regex); - }); - }); - - it('Displays the correct timeline details inside the query tab', () => { - let expectedQueryTab = timelineDetails.queryTab; - if (semver.lt(Cypress.env('ORIGINAL_VERSION'), '7.10.0')) { - expectedQueryTab = timelineDetails.queryTabAlt; - } - - openTimeline(); - - cy.readFile(`cypress/fixtures/${timeline}`).then((file) => { - const timelineJson = JSON.parse(file); - - cy.get(TIMELINE_TITLE).should('have.text', timelineJson.title); - cy.get(TIMELINE_DESCRIPTION).should('have.text', timelineJson.description); - cy.get(DATA_PROVIDERS).should('have.length', timelineJson.dataProviders.length.toString()); - cy.get(DATA_PROVIDERS) - .invoke('text') - .then((value) => { - expect(value.replace(/"/g, '')).to.eq(timelineJson.dataProviders[0].name); - }); - cy.get(PROCESS_KPI).should('contain', '0'); - cy.get(USER_KPI).should('contain', '0'); - cy.get(HOST_KPI).should('contain', '1'); - cy.get(SOURCE_IP_KPI).should('contain', '1'); - cy.get(DESTINATION_IP_KPI).should('contain', '1'); - cy.get(DATE_PICKER_START).should('contain', timelineDetails.dateStart); - cy.get(DATE_PICKER_END).should('contain', timelineDetails.dateEnd); - cy.get(TIMELINE_QUERY).should( - 'have.text', - timelineJson.kqlQuery.filterQuery.kuery.expression - ); - cy.get(QUERY_TAB_BUTTON).should('have.text', expectedQueryTab); - cy.get(TIMELINE_CORRELATION_TAB).should('have.text', timelineDetails.correlationTab); - cy.get(GRAPH_TAB_BUTTON).should('have.text', timelineDetails.analyzerTab).and('be.disabled'); - cy.get(NOTES_TAB_BUTTON).should('have.text', timelineDetails.notesTab); - cy.get(PINNED_TAB_BUTTON).should('have.text', timelineDetails.pinnedTab); - - cy.get(QUERY_EVENT_TABLE_CELL).eq(1).should('contain', detectionAlert.message); - cy.get(QUERY_EVENT_TABLE_CELL).eq(2).should('contain', detectionAlert.eventCategory); - cy.get(QUERY_EVENT_TABLE_CELL).eq(3).should('contain', detectionAlert.eventAction); - cy.get(QUERY_EVENT_TABLE_CELL).eq(4).should('contain', detectionAlert.hostName); - cy.get(QUERY_EVENT_TABLE_CELL).eq(5).should('contain', detectionAlert.sourceIp); - cy.get(QUERY_EVENT_TABLE_CELL).eq(6).should('contain', detectionAlert.destinationIp); - cy.get(QUERY_EVENT_TABLE_CELL).eq(7).should('contain', detectionAlert.userName); - - cy.get(QUERY_EVENT_TABLE_CELL).eq(8).should('contain', event.timestamp); - cy.get(QUERY_EVENT_TABLE_CELL).eq(9).should('contain', event.message); - cy.get(QUERY_EVENT_TABLE_CELL).eq(10).should('contain', event.eventCategory); - cy.get(QUERY_EVENT_TABLE_CELL).eq(11).should('contain', event.eventAction); - cy.get(QUERY_EVENT_TABLE_CELL).eq(12).should('contain', event.hostName); - cy.get(QUERY_EVENT_TABLE_CELL).eq(13).should('contain', event.sourceIp); - cy.get(QUERY_EVENT_TABLE_CELL).eq(14).should('contain', event.destinationIp); - cy.get(QUERY_EVENT_TABLE_CELL).eq(15).should('contain', event.userName); - }); - }); - - it('Displays the correct timeline details inside the correlation tab', () => { - goToCorrelationTab(); - - cy.get(TIMELINE_CORRELATION_INPUT).should('be.empty'); - cy.get(CORRELATION_EVENT_TABLE_CELL).should('not.exist'); - }); - - it('Displays the correct timeline details inside the notes tab', () => { - goToNotesTab(); - - cy.readFile(`cypress/fixtures/${timeline}`).then((file) => { - const timelineJson = JSON.parse(file); - const descriptionRegex = new RegExp( - `\\S${username}added description\\S*\\s?(\\S*)?\\s?(\\S*)?${timelineJson.description}` - ); - const noteRegex = new RegExp( - `\\S${timelineJson.globalNotes[0].createdBy}added a note\\S*\\s?(\\S*)?\\s?(\\S*)?${timelineJson.globalNotes[0].createdBy} added a note${timelineJson.globalNotes[0].note}` - ); - - cy.get(NOTE_DESCRIPTION).invoke('text').should('match', descriptionRegex); - cy.get(NOTE_PREVIEW).last().invoke('text').should('match', noteRegex); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index a564416154176..19c02e030d391 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -8,23 +8,19 @@ "extract-mitre-attacks": "node scripts/extract_tactics_techniques_mitre.js && node ../../../scripts/eslint ./public/detections/mitre/mitre_tactics_techniques.ts --fix", "build-beat-doc": "node scripts/beat_docs/build.js && node ../../../scripts/eslint ../timelines/server/utils/beat_schema/fields.ts --fix", "cypress": "../../../node_modules/.bin/cypress", + "cypress:burn": "yarn cypress:run:reporter --env burn=2 --concurrency=1 --headed", + "cypress:changed-specs-only": "yarn cypress:run:reporter --changed-specs-only --env burn=2", "cypress:open": "TZ=UTC node ./scripts/start_cypress_parallel open --spec './cypress/e2e/**/*.cy.ts' --config-file ./cypress/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config", - "cypress:open:ccs": "yarn cypress:open --config specPattern=./cypress/ccs_e2e/**/*.cy.ts", - "cypress:open:upgrade": "yarn cypress:open --config specPattern=./cypress/upgrade_e2e/**/*.cy.ts", - "cypress:run": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/{,!(investigations,explore)/**/}*.cy.ts'; status=$?; yarn junit:merge && exit $status", - "cypress:run:cases": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/explore/cases/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", - "cypress:run:firefox": "yarn cypress:run:reporter --browser firefox --spec './cypress/e2e/**/*.cy.ts'; status=$?; yarn junit:merge && exit $status", - "cypress:run:reporter": "TZ=UTC node ./scripts/start_cypress_parallel run --config-file ./cypress/cypress_ci.config.ts --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json", - "cypress:run:respops": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/(detection_alerts|detection_rules|exceptions)/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", - "cypress:run:ccs": "yarn cypress:run:reporter --browser chrome --config specPattern=./cypress/ccs_e2e/**/*.cy.ts; status=$?; yarn junit:merge && exit $status", - "cypress:run-as-ci:firefox": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/security_solution_cypress/config.firefox.ts", - "cypress:run:upgrade": "yarn cypress:run:reporter --browser chrome --config specPattern=./cypress/upgrade_e2e/**/*.cy.ts", + "cypress:run": "yarn cypress:run:reporter --spec './cypress/e2e/{,!(investigations,explore)/**/}*.cy.ts'; status=$?; yarn junit:merge && exit $status", + "cypress:run:cases": "yarn cypress:run:reporter --spec './cypress/e2e/explore/cases/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", + "cypress:run:reporter": "TZ=UTC node ./scripts/start_cypress_parallel run --browser chrome --config-file ./cypress/cypress_ci.config.ts --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json", + "cypress:run:respops": "yarn cypress:run:reporter --spec './cypress/e2e/(detection_alerts|detection_rules|exceptions)/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", "cypress:dw:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress.config.ts ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/cli_config", "cypress:dw:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; yarn junit:merge && exit $status", "cypress:dw:endpoint:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress_endpoint.config.ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/endpoint_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json --concurrency 1; status=$?; yarn junit:merge && exit $status", "cypress:dw:endpoint:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress_endpoint.config.ts ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/endpoint_config", - "cypress:investigations:run": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/investigations/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", - "cypress:explore:run": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/explore/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", + "cypress:investigations:run": "yarn cypress:run:reporter --spec './cypress/e2e/investigations/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", + "cypress:explore:run": "yarn cypress:run:reporter --spec './cypress/e2e/explore/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status", "junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results && yarn junit:transform && mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/", "test:generate": "node scripts/endpoint/resolver_generator", "mappings:generate": "node scripts/mappings/mappings_generator", diff --git a/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx b/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx index a1ca43bbcb93e..85fbcb8284cb5 100644 --- a/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx @@ -10,24 +10,23 @@ import { EuiHeaderSection, EuiHeaderSectionItem, } from '@elastic/eui'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { i18n } from '@kbn/i18n'; import type { AppMountParameters } from '@kbn/core/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import { useVariation } from '../../../common/components/utils'; import { MlPopover } from '../../../common/components/ml_popover/ml_popover'; import { useKibana } from '../../../common/lib/kibana'; -import { ADD_DATA_PATH, ADD_THREAT_INTELLIGENCE_DATA_PATH } from '../../../../common/constants'; -import { isDetectionsPath, isThreatIntelligencePath } from '../../../helpers'; +import { isDetectionsPath } from '../../../helpers'; import { Sourcerer } from '../../../common/components/sourcerer'; import { TimelineId } from '../../../../common/types/timeline'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { timelineSelectors } from '../../../timelines/store/timeline'; import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { getScopeFromPath, showSourcererByPath } from '../../../common/containers/sourcerer'; +import { useAddIntegrationsUrl } from '../../../common/hooks/use_add_integrations_url'; const BUTTON_ADD_DATA = i18n.translate('xpack.securitySolution.globalHeader.buttonAddData', { defaultMessage: 'Add integrations', @@ -40,13 +39,7 @@ const BUTTON_ADD_DATA = i18n.translate('xpack.securitySolution.globalHeader.butt export const GlobalHeader = React.memo( ({ setHeaderActionMenu }: { setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'] }) => { const portalNode = useMemo(() => createHtmlPortalNode(), []); - const { - theme, - http: { - basePath: { prepend }, - }, - cloudExperiments, - } = useKibana().services; + const { theme } = useKibana().services; const { pathname } = useLocation(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); @@ -57,18 +50,7 @@ export const GlobalHeader = React.memo( const sourcererScope = getScopeFromPath(pathname); const showSourcerer = showSourcererByPath(pathname); - const integrationsUrl = isThreatIntelligencePath(pathname) - ? ADD_THREAT_INTELLIGENCE_DATA_PATH - : ADD_DATA_PATH; - const [addIntegrationsUrl, setAddIntegrationsUrl] = useState(integrationsUrl); - useVariation( - cloudExperiments, - 'security-solutions.add-integrations-url', - integrationsUrl, - setAddIntegrationsUrl - ); - - const href = useMemo(() => prepend(addIntegrationsUrl), [prepend, addIntegrationsUrl]); + const { href, onClick } = useAddIntegrationsUrl(); useEffect(() => { setHeaderActionMenu((element) => { @@ -98,6 +80,7 @@ export const GlobalHeader = React.memo( data-test-subj="add-data" href={href} iconType="indexOpen" + onClick={onClick} > {BUTTON_ADD_DATA} diff --git a/x-pack/plugins/security_solution/public/cases_test_utils.ts b/x-pack/plugins/security_solution/public/cases_test_utils.ts index 8dd64424e41e5..d177934cb02ee 100644 --- a/x-pack/plugins/security_solution/public/cases_test_utils.ts +++ b/x-pack/plugins/security_solution/public/cases_test_utils.ts @@ -11,6 +11,7 @@ export const noCasesCapabilities = () => ({ update_cases: false, delete_cases: false, push_cases: false, + cases_connector: false, }); export const readCasesCapabilities = () => ({ @@ -19,6 +20,7 @@ export const readCasesCapabilities = () => ({ update_cases: false, delete_cases: false, push_cases: false, + cases_connector: true, }); export const allCasesCapabilities = () => ({ @@ -27,6 +29,7 @@ export const allCasesCapabilities = () => ({ update_cases: true, delete_cases: true, push_cases: true, + cases_connector: true, }); export const noCasesPermissions = () => ({ @@ -36,6 +39,7 @@ export const noCasesPermissions = () => ({ update: false, delete: false, push: false, + connectors: false, }); export const readCasesPermissions = () => ({ @@ -45,6 +49,7 @@ export const readCasesPermissions = () => ({ update: false, delete: false, push: false, + connectors: true, }); export const writeCasesPermissions = () => ({ @@ -54,6 +59,7 @@ export const writeCasesPermissions = () => ({ update: true, delete: true, push: true, + connectors: true, }); export const allCasesPermissions = () => ({ @@ -63,4 +69,5 @@ export const allCasesPermissions = () => ({ update: true, delete: true, push: true, + connectors: true, }); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx index eadf94552794e..943bf4dab0d80 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx @@ -187,7 +187,7 @@ const LensEmbeddableComponent: React.FC = ({ const onFilterCallback = useCallback( async (e: ClickTriggerEvent['data'] | MultiClickTriggerEvent['data']) => { - if (!Array.isArray(e.data) || preferredSeriesType !== 'area') { + if (!isClickTriggerEvent(e) || preferredSeriesType !== 'area') { return; } // Update timerange when clicking on a dot in an area chart @@ -301,4 +301,10 @@ const LensEmbeddableComponent: React.FC = ({ ); }; +const isClickTriggerEvent = ( + e: ClickTriggerEvent['data'] | MultiClickTriggerEvent['data'] +): e is ClickTriggerEvent['data'] => { + return Array.isArray(e.data) && 'column' in e.data[0]; +}; + export const LensEmbeddable = React.memo(LensEmbeddableComponent); diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx index 84c94f81d026f..c5251dba05744 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx @@ -67,6 +67,7 @@ describe('source/index.tsx', () => { }); }, getFieldsForWildcard: async () => Promise.resolve(), + getExistingIndices: async (indices: string[]) => Promise.resolve(indices), }, search: { search: jest.fn().mockReturnValue({ diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/get_sourcerer_data_view.test.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/get_sourcerer_data_view.test.ts new file mode 100644 index 0000000000000..cc4921380fac8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/get_sourcerer_data_view.test.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getSourcererDataView } from './get_sourcerer_data_view'; +import type { DataViewsService } from '@kbn/data-views-plugin/common'; + +const dataViewId = 'test-id'; +const dataViewsService = { + get: jest.fn().mockResolvedValue({ + toSpec: jest.fn().mockReturnValue({ + id: 'test-id', + fields: {}, + runtimeFieldMap: {}, + }), + getIndexPattern: jest.fn().mockReturnValue('test-pattern'), + fields: {}, + }), + getExistingIndices: jest.fn().mockResolvedValue(['test-pattern']), +} as unknown as jest.Mocked; +describe('getSourcererDataView', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + // Tests that the function returns a SourcererDataView object with the expected properties + it('should return a SourcererDataView object with the expected properties', async () => { + const result = await getSourcererDataView(dataViewId, dataViewsService); + expect(result).toEqual({ + loading: false, + id: 'test-id', + title: 'test-pattern', + indexFields: {}, + fields: {}, + patternList: ['test-pattern'], + dataView: { + id: 'test-id', + fields: {}, + runtimeFieldMap: {}, + }, + browserFields: {}, + runtimeMappings: {}, + }); + }); + it('should call dataViewsService.get with the correct arguments', async () => { + await getSourcererDataView(dataViewId, dataViewsService); + expect(dataViewsService.get).toHaveBeenCalledWith(dataViewId, true, false); + }); + + it('should call dataViewsService.getExistingIndices with the correct arguments', async () => { + await getSourcererDataView(dataViewId, dataViewsService); + expect(dataViewsService.getExistingIndices).toHaveBeenCalledWith(['test-pattern']); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/get_sourcerer_data_view.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/get_sourcerer_data_view.ts index 7f40819bfb41d..a3cc1a8041b4c 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/get_sourcerer_data_view.ts +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/get_sourcerer_data_view.ts @@ -18,30 +18,7 @@ export const getSourcererDataView = async ( const dataView = await dataViewsService.get(dataViewId, true, refreshFields); const dataViewData = dataView.toSpec(); const defaultPatternsList = ensurePatternFormat(dataView.getIndexPattern().split(',')); - - // typeguard used to assert that pattern is a string, otherwise - // typescript expects patternList to be (string | null)[] - // but we want it to always be string[] - const filterTypeGuard = (str: unknown): str is string => str != null; - const patternList = await Promise.all( - defaultPatternsList.map(async (pattern) => { - try { - await dataViewsService.getFieldsForWildcard({ - type: dataViewData.type, - rollupIndex: dataViewData?.typeMeta?.params?.rollup_index, - allowNoIndex: false, - pattern, - }); - return pattern; - } catch { - return null; - } - }) - ) - .then((allPatterns) => - allPatterns.filter((pattern): pattern is string => filterTypeGuard(pattern)) - ) - .catch(() => defaultPatternsList); + const patternList = await dataViewsService.getExistingIndices(defaultPatternsList); return { loading: false, diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_add_integrations_url.ts b/x-pack/plugins/security_solution/public/common/hooks/use_add_integrations_url.ts new file mode 100644 index 0000000000000..0bbedbb3adadb --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_add_integrations_url.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useCallback, useMemo, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { ADD_DATA_PATH, ADD_THREAT_INTELLIGENCE_DATA_PATH } from '../../../common/constants'; +import { isThreatIntelligencePath } from '../../helpers'; +import { useVariation } from '../components/utils'; + +import { useKibana, useNavigateTo } from '../lib/kibana'; + +export const useAddIntegrationsUrl = () => { + const { + http: { + basePath: { prepend }, + }, + cloudExperiments, + } = useKibana().services; + const { pathname } = useLocation(); + const { navigateTo } = useNavigateTo(); + + const isThreatIntelligence = isThreatIntelligencePath(pathname); + + const integrationsUrl = isThreatIntelligence ? ADD_THREAT_INTELLIGENCE_DATA_PATH : ADD_DATA_PATH; + const [addIntegrationsUrl, setAddIntegrationsUrl] = useState(integrationsUrl); + useVariation( + cloudExperiments, + 'security-solutions.add-integrations-url', + integrationsUrl, + setAddIntegrationsUrl + ); + + const href = useMemo(() => prepend(addIntegrationsUrl), [prepend, addIntegrationsUrl]); + + const onClick = useCallback( + (e) => { + e.preventDefault(); + navigateTo({ url: href }); + }, + [href, navigateTo] + ); + + return { + href, + onClick, + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx index 99aae028a7e51..6469b0bcffb17 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx @@ -43,6 +43,7 @@ describe('use_upselling', () => { wrapper: RenderWrapper, }); expect(result.current).toBe(TestComponent); + expect(result.all.length).toBe(1); // assert that it should not cause unnecessary re-renders }); test('useUpsellingPage returns pages', () => { @@ -66,6 +67,7 @@ describe('use_upselling', () => { wrapper: RenderWrapper, }); expect(result.current).toBe(testMessage); + expect(result.all.length).toBe(1); // assert that it should not cause unnecessary re-renders }); test('useUpsellingMessage returns null when upsellingMessageId not found', () => { diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts index e54053978fe03..03f5935a85e0f 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts @@ -15,14 +15,14 @@ import type { UpsellingMessageId } from '../lib/upsellings/types'; export const useUpsellingComponent = (id: UpsellingSectionId): React.ComponentType | null => { const upselling = useUpsellingService(); - const upsellingSections = useObservable(upselling.sections$); + const upsellingSections = useObservable(upselling.sections$, upselling.getSectionsValue()); return useMemo(() => upsellingSections?.get(id) ?? null, [id, upsellingSections]); }; export const useUpsellingMessage = (id: UpsellingMessageId): string | null => { const upselling = useUpsellingService(); - const upsellingMessages = useObservable(upselling.messages$); + const upsellingMessages = useObservable(upselling.messages$, upselling.getMessagesValue()); return useMemo(() => upsellingMessages?.get(id) ?? null, [id, upsellingMessages]); }; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts index 1713f35a9a2d2..043d1a0ab36f0 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts @@ -161,6 +161,7 @@ export const useGetUserCasesPermissions = () => { update: false, delete: false, push: false, + connectors: false, }); const uiCapabilities = useKibana().services.application.capabilities; const casesCapabilities = useKibana().services.cases.helpers.getUICapabilities( @@ -175,6 +176,7 @@ export const useGetUserCasesPermissions = () => { update: casesCapabilities.update, delete: casesCapabilities.delete, push: casesCapabilities.push, + connectors: casesCapabilities.connectors, }); }, [ casesCapabilities.all, @@ -183,6 +185,7 @@ export const useGetUserCasesPermissions = () => { casesCapabilities.update, casesCapabilities.delete, casesCapabilities.push, + casesCapabilities.connectors, ]); return casesPermissions; diff --git a/x-pack/plugins/security_solution/public/common/lib/upsellings/upselling_service.ts b/x-pack/plugins/security_solution/public/common/lib/upsellings/upselling_service.ts index 7e7610a2df68e..14d4949e8ea4b 100644 --- a/x-pack/plugins/security_solution/public/common/lib/upsellings/upselling_service.ts +++ b/x-pack/plugins/security_solution/public/common/lib/upsellings/upselling_service.ts @@ -71,4 +71,12 @@ export class UpsellingService { getPageUpselling(id: SecurityPageName) { return this.pages.get(id); } + + getSectionsValue() { + return this.sectionsSubject$.getValue(); + } + + getMessagesValue() { + return this.messagesSubject$.getValue(); + } } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.test.tsx index b2a6c047f03d9..b37c9d489029f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.test.tsx @@ -29,8 +29,7 @@ const mockKibanaHttpService = coreMock.createStart().http; jest.mock('../../rule_management/api/api'); jest.mock('@kbn/securitysolution-list-api'); -// Broken during Jest 29 upgrade -describe.skip('useFetchOrCreateRuleExceptionList', () => { +describe('useFetchOrCreateRuleExceptionList', () => { let fetchRuleById: jest.SpyInstance>; let patchRule: jest.SpyInstance>; let addExceptionList: jest.SpyInstance>; @@ -112,20 +111,13 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { }); it('initializes hook', async () => { - await act(async () => { - const { result, waitForNextUpdate } = render(); - await waitForNextUpdate(); - expect(result.current).toEqual([false, null]); - }); - }); + const { result, waitForNextUpdate } = render(); - it('sets isLoading to true while fetching', async () => { - await act(async () => { - const { result, waitForNextUpdate } = render(); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual([true, null]); - }); + // Should set isLoading to true while fetching + expect(result.current).toEqual([true, null]); + + await waitForNextUpdate(); + expect(result.current).toEqual([false, detectionExceptionList]); }); it('fetches the rule with the given ruleId', async () => { @@ -133,7 +125,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(fetchRuleById).toHaveBeenCalledTimes(1); expect(fetchRuleById).toHaveBeenCalledWith({ id: ruleId, @@ -153,8 +144,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); expect(fetchExceptionListById).not.toHaveBeenCalled(); }); }); @@ -163,7 +152,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(addExceptionList).toHaveBeenCalledTimes(1); }); }); @@ -193,7 +181,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(fetchExceptionListById).toHaveBeenCalledTimes(2); }); }); @@ -201,8 +188,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); expect(addExceptionList).not.toHaveBeenCalled(); }); }); @@ -210,8 +195,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); expect(patchRule).not.toHaveBeenCalled(); }); }); @@ -220,7 +203,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { result, waitForNextUpdate } = render(); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(result.current[1]).toEqual(detectionExceptionList); }); }); @@ -229,7 +211,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(onSuccess).toHaveBeenCalledWith(false); }); }); @@ -246,7 +227,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(addExceptionList).toHaveBeenCalledTimes(1); }); }); @@ -287,7 +267,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { waitForNextUpdate } = render(endpointListType); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(fetchExceptionListById).toHaveBeenCalledTimes(2); }); }); @@ -295,8 +274,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { waitForNextUpdate } = render(endpointListType); await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); expect(addExceptionList).not.toHaveBeenCalled(); }); }); @@ -304,8 +281,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { waitForNextUpdate } = render(endpointListType); await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); expect(patchRule).not.toHaveBeenCalled(); }); }); @@ -314,7 +289,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { result, waitForNextUpdate } = render(endpointListType); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(result.current[1]).toEqual(endpointExceptionList); }); }); @@ -331,7 +305,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { const { waitForNextUpdate } = render(endpointListType); await waitForNextUpdate(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(addEndpointExceptionList).toHaveBeenCalledTimes(1); }); }); @@ -365,7 +338,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { result, waitForNextUpdate } = render(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(result.current[1]).toBeNull(); }); }); @@ -374,7 +346,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { result, waitForNextUpdate } = render(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(result.current[0]).toEqual(false); }); }); @@ -393,7 +364,6 @@ describe.skip('useFetchOrCreateRuleExceptionList', () => { await act(async () => { const { waitForNextUpdate } = render(); await waitForNextUpdate(); - await waitForNextUpdate(); expect(onSuccess).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx index b5984d66a5f56..70fc7554e64cd 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx @@ -37,7 +37,6 @@ import { EventKind } from '../../shared/hooks/use_fetch_field_value_pair_by_even interface PrevalenceDetailsTableCell { highlightedField: { name: string; values: string[] }; - scopeId: string; } export const PREVALENCE_TAB_ID = 'prevalence-details'; @@ -60,7 +59,6 @@ const columns: Array> = [ render: (data: PrevalenceDetailsTableCell) => ( > = [ render: (data: PrevalenceDetailsTableCell) => ( > = [ render: (data: PrevalenceDetailsTableCell) => ( ), @@ -102,7 +98,6 @@ const columns: Array> = [ render: (data: PrevalenceDetailsTableCell) => ( ), @@ -129,7 +124,6 @@ export const PrevalenceDetails: React.FC = () => { name: summaryRow.description.data.field, values: summaryRow.description.values || [], }, - scopeId, }); return (summaryRows || []).map((summaryRow) => { diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.test.tsx index 31f56ada7b5f2..5b81565ec2804 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.test.tsx @@ -24,7 +24,6 @@ const highlightedField = { name: 'field', values: ['values'], }; -const scopeId = 'scopeId'; const type = { eventKind: EventKind.signal, include: true, @@ -39,11 +38,7 @@ describe('PrevalenceDetailsAlertCountCell', () => { }); const { getByTestId } = render( - + ); expect(getByTestId(PREVALENCE_DETAILS_COUNT_CELL_LOADING_TEST_ID)).toBeInTheDocument(); @@ -57,11 +52,7 @@ describe('PrevalenceDetailsAlertCountCell', () => { }); const { getByTestId } = render( - + ); expect(getByTestId(PREVALENCE_DETAILS_COUNT_CELL_ERROR_TEST_ID)).toBeInTheDocument(); @@ -75,11 +66,7 @@ describe('PrevalenceDetailsAlertCountCell', () => { }); const { getByTestId } = render( - + ); expect(getByTestId(PREVALENCE_DETAILS_COUNT_CELL_VALUE_TEST_ID)).toBeInTheDocument(); @@ -94,11 +81,7 @@ describe('PrevalenceDetailsAlertCountCell', () => { }); const { getByTestId } = render( - + ); expect(getByTestId(PREVALENCE_DETAILS_COUNT_CELL_VALUE_TEST_ID)).toBeInTheDocument(); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.tsx index 72c110de353c6..a2e3959a9b1c6 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_count_cell.tsx @@ -15,17 +15,12 @@ import { } from './test_ids'; import type { EventType } from '../../shared/hooks/use_fetch_field_value_pair_by_event_type'; import { useFetchFieldValuePairByEventType } from '../../shared/hooks/use_fetch_field_value_pair_by_event_type'; -import { TimelineId } from '../../../../common/types'; export interface PrevalenceDetailsCountCellProps { /** * The highlighted field name and values * */ highlightedField: { name: string; values: string[] }; - /** - * The scope id - */ - scopeId: string; /** * Limit the search to include or exclude a specific value for the event.kind field * (alert, asset, enrichment, event, metric, state, pipeline_error, signal) @@ -41,12 +36,10 @@ export interface PrevalenceDetailsCountCellProps { */ export const PrevalenceDetailsCountCell: VFC = ({ highlightedField, - scopeId, type, }) => { const { loading, error, count } = useFetchFieldValuePairByEventType({ highlightedField, - isActiveTimelines: scopeId === TimelineId.active, type, }); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.test.tsx index 3837162672bfb..b4bf809665f98 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.test.tsx @@ -23,7 +23,6 @@ const highlightedField = { name: 'field', values: ['values'], }; -const scopeId = 'scopeId'; const aggregationField = 'aggregationField'; describe('PrevalenceDetailsAlertCountCell', () => { @@ -42,7 +41,6 @@ describe('PrevalenceDetailsAlertCountCell', () => { const { getByTestId } = render( ); @@ -65,7 +63,6 @@ describe('PrevalenceDetailsAlertCountCell', () => { const { getByTestId } = render( ); @@ -88,7 +85,6 @@ describe('PrevalenceDetailsAlertCountCell', () => { const { getByTestId } = render( ); @@ -111,7 +107,6 @@ describe('PrevalenceDetailsAlertCountCell', () => { const { getByTestId } = render( ); @@ -134,7 +129,6 @@ describe('PrevalenceDetailsAlertCountCell', () => { const { getByTestId } = render( ); @@ -157,7 +151,6 @@ describe('PrevalenceDetailsAlertCountCell', () => { const { getByTestId } = render( ); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.tsx index ad8fa2e49289c..9c05bc8e8ea32 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details_prevalence_cell.tsx @@ -14,7 +14,6 @@ import { PREVALENCE_DETAILS_PREVALENCE_CELL_VALUE_TEST_ID, } from './test_ids'; import { useFetchFieldValuePairWithAggregation } from '../../shared/hooks/use_fetch_field_value_pair_with_aggregation'; -import { TimelineId } from '../../../../common/types'; import { useFetchUniqueByField } from '../../shared/hooks/use_fetch_unique_by_field'; export interface PrevalenceDetailsPrevalenceCellProps { @@ -22,10 +21,6 @@ export interface PrevalenceDetailsPrevalenceCellProps { * The highlighted field name and values * */ highlightedField: { name: string; values: string[] }; - /** - * The scope id - */ - scopeId: string; /** * The aggregation field */ @@ -38,7 +33,6 @@ export interface PrevalenceDetailsPrevalenceCellProps { */ export const PrevalenceDetailsPrevalenceCell: VFC = ({ highlightedField, - scopeId, aggregationField, }) => { const { @@ -47,7 +41,6 @@ export const PrevalenceDetailsPrevalenceCell: VFC { dataFormattedForFieldBrowser, isAlert, }); + const showShareAlertButton = isAlert && alertUrl; return ( <> - - - -

- {isAlert && !isEmpty(ruleName) ? ruleName : DOCUMENT_DETAILS} -

-
- - - {isAlert && alertUrl && ( - - - - )} - {showAssistant && ( - - - - )} - - + {(showShareAlertButton || showAssistant) && ( + + {showAssistant && ( + + + + )} + {showShareAlertButton && ( + + + + )} + )} + + +

+ {isAlert && !isEmpty(ruleName) ? ruleName : DOCUMENT_DETAILS} +

- + @@ -77,7 +84,7 @@ export const HeaderTitle: FC = memo(() => { {timestamp && } - + diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview.test.tsx index 13f51e47c6a78..55e635097311f 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview.test.tsx @@ -26,7 +26,6 @@ const highlightedField = { name: 'field', values: ['values'], }; -const scopeId = 'scopeId'; const callbackIfNull = jest.fn(); const panelContextValue = { @@ -61,7 +60,6 @@ describe('', () => { prevalenceRows: [ , @@ -106,7 +104,6 @@ describe('', () => { prevalenceRows: [ , diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.test.tsx index ac6a76e606444..fc1a4ce2b1a3f 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.test.tsx @@ -18,7 +18,6 @@ const highlightedField = { name: 'field', values: ['values'], }; -const scopeId = 'scopeId'; const dataTestSubj = 'test'; const iconDataTestSubj = 'testIcon'; const valueDataTestSubj = 'testValue'; @@ -41,7 +40,6 @@ describe('', () => { const { getByTestId, getAllByText, queryByTestId } = render( {}} data-test-subj={dataTestSubj} /> @@ -71,7 +69,6 @@ describe('', () => { const { queryAllByAltText } = render( @@ -97,7 +94,6 @@ describe('', () => { const { queryAllByAltText } = render( @@ -122,7 +118,6 @@ describe('', () => { const { getByTestId } = render( {}} data-test-subj={dataTestSubj} /> diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.tsx index 0fd37af5929e8..d7cef2fe99f8b 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/prevalence_overview_row.tsx @@ -11,7 +11,6 @@ import { PREVALENCE_ROW_UNCOMMON } from './translations'; import { useFetchFieldValuePairWithAggregation } from '../../shared/hooks/use_fetch_field_value_pair_with_aggregation'; import { useFetchUniqueByField } from '../../shared/hooks/use_fetch_unique_by_field'; import { InsightsSummaryRow } from './insights_summary_row'; -import { TimelineId } from '../../../../common/types'; const HOST_FIELD = 'host.name'; const PERCENTAGE_THRESHOLD = 0.1; // we show the prevalence if its value is below 10% @@ -21,10 +20,6 @@ export interface PrevalenceOverviewRowProps { * The highlighted field name and values * */ highlightedField: { name: string; values: string[] }; - /** - * Maintain backwards compatibility // TODO remove when possible - */ - scopeId: string; /** * This is a solution to allow the parent component to NOT render if all its row children are null */ @@ -42,19 +37,15 @@ export interface PrevalenceOverviewRowProps { */ export const PrevalenceOverviewRow: VFC = ({ highlightedField, - scopeId, callbackIfNull, 'data-test-subj': dataTestSubj, }) => { - const isActiveTimelines = scopeId === TimelineId.active; - const { loading: hostsLoading, error: hostsError, count: hostsCount, } = useFetchFieldValuePairWithAggregation({ highlightedField, - isActiveTimelines, aggregationField: HOST_FIELD, }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts index daa6eaf4787ca..03b3c8d4b9d80 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts @@ -11,12 +11,12 @@ import { i18n } from '@kbn/i18n'; export const EXPAND_DETAILS_BUTTON = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.expandDetailButton', - { defaultMessage: 'Expand alert details' } + { defaultMessage: 'Expand details' } ); export const COLLAPSE_DETAILS_BUTTON = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.collapseDetailButton', - { defaultMessage: 'Collapse alert details' } + { defaultMessage: 'Collapse details' } ); export const DOCUMENT_DETAILS = i18n.translate( diff --git a/x-pack/plugins/security_solution/public/flyout/right/header.tsx b/x-pack/plugins/security_solution/public/flyout/right/header.tsx index 6a9194f892fd3..4f316b9a8be50 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/header.tsx @@ -42,6 +42,7 @@ export const PanelHeader: VFC = memo( `} >
= memo( >
- + setCount((prevCount) => prevCount + 1)} data-test-subj={INSIGHTS_PREVALENCE_TEST_ID} key={row.description.data.field} /> ); }), - [summaryRows, scopeId] + [summaryRows] ); return { diff --git a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.test.ts b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.test.ts index 8bbd039ff333a..bac2f7b37af63 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.test.ts +++ b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.test.ts @@ -9,8 +9,6 @@ import { useQuery } from '@tanstack/react-query'; import type { RenderHookResult } from '@testing-library/react-hooks'; import { renderHook } from '@testing-library/react-hooks'; import { useKibana } from '../../../common/lib/kibana'; -import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { useGlobalTime } from '../../../common/containers/use_global_time'; import type { UseFetchFieldValuePairByEventTypeParams, UseFetchFieldValuePairByEventTypeResult, @@ -22,14 +20,11 @@ import { jest.mock('@tanstack/react-query'); jest.mock('../../../common/lib/kibana'); -jest.mock('../../../common/hooks/use_selector'); -jest.mock('../../../common/containers/use_global_time'); const highlightedField = { name: 'field', values: ['values'], }; -const isActiveTimelines = true; const type = { eventKind: EventKind.alert, include: true, @@ -45,8 +40,6 @@ describe('useFetchFieldValuePairByEventType', () => { data: { search: jest.fn() }, }, }); - jest.mocked(useDeepEqualSelector).mockReturnValue({ to: '', from: '' }); - (useGlobalTime as jest.Mock).mockReturnValue({ to: '', from: '' }); it('should return loading true while data is being fetched', () => { (useQuery as jest.Mock).mockReturnValue({ @@ -55,9 +48,7 @@ describe('useFetchFieldValuePairByEventType', () => { data: 0, }); - hookResult = renderHook(() => - useFetchFieldValuePairByEventType({ highlightedField, isActiveTimelines, type }) - ); + hookResult = renderHook(() => useFetchFieldValuePairByEventType({ highlightedField, type })); expect(hookResult.result.current.loading).toBeTruthy(); expect(hookResult.result.current.error).toBeFalsy(); @@ -71,9 +62,7 @@ describe('useFetchFieldValuePairByEventType', () => { data: 0, }); - hookResult = renderHook(() => - useFetchFieldValuePairByEventType({ highlightedField, isActiveTimelines, type }) - ); + hookResult = renderHook(() => useFetchFieldValuePairByEventType({ highlightedField, type })); expect(hookResult.result.current.loading).toBeFalsy(); expect(hookResult.result.current.error).toBeTruthy(); @@ -87,9 +76,7 @@ describe('useFetchFieldValuePairByEventType', () => { data: 1, }); - hookResult = renderHook(() => - useFetchFieldValuePairByEventType({ highlightedField, isActiveTimelines, type }) - ); + hookResult = renderHook(() => useFetchFieldValuePairByEventType({ highlightedField, type })); expect(hookResult.result.current.loading).toBeFalsy(); expect(hookResult.result.current.error).toBeFalsy(); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.ts b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.ts index 4f8312c5bec0e..c1e52b90558e5 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.ts +++ b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_by_event_type.ts @@ -10,12 +10,11 @@ import type { IEsSearchRequest } from '@kbn/data-plugin/public'; import { useQuery } from '@tanstack/react-query'; import { createFetchData } from '../utils/fetch_data'; import { useKibana } from '../../../common/lib/kibana'; -import { inputsSelectors } from '../../../common/store'; -import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { useGlobalTime } from '../../../common/containers/use_global_time'; import type { RawResponse } from '../utils/fetch_data'; const QUERY_KEY = 'FetchFieldValuePairByEventType'; +const DEFAULT_FROM = 'now-30d'; +const DEFAULT_TO = 'now'; export enum EventKind { alert = 'alert', @@ -39,10 +38,6 @@ export interface UseFetchFieldValuePairByEventTypeParams { * The highlighted field name and values * */ highlightedField: { name: string; values: string[] }; - /** - * True is the current timeline is active ('timeline-1') - */ - isActiveTimelines: boolean; /** * Limit the search to include or exclude a specific value for the event.kind field * (alert, asset, enrichment, event, metric, state, pipeline_error, signal) @@ -70,7 +65,6 @@ export interface UseFetchFieldValuePairByEventTypeResult { */ export const useFetchFieldValuePairByEventType = ({ highlightedField, - isActiveTimelines, type, }: UseFetchFieldValuePairByEventTypeParams): UseFetchFieldValuePairByEventTypeResult => { const { @@ -79,11 +73,7 @@ export const useFetchFieldValuePairByEventType = ({ }, } = useKibana(); - const timelineTime = useDeepEqualSelector((state) => - inputsSelectors.timelineTimeRangeSelector(state) - ); - const globalTime = useGlobalTime(); - const { to, from } = isActiveTimelines ? timelineTime : globalTime; + const { from, to } = { from: DEFAULT_FROM, to: DEFAULT_TO }; const { name, values } = highlightedField; diff --git a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.test.ts b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.test.ts index a1ec575db1244..7de92d29cc17e 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.test.ts +++ b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.test.ts @@ -14,19 +14,14 @@ import type { UseFetchFieldValuePairWithAggregationResult, } from './use_fetch_field_value_pair_with_aggregation'; import { useFetchFieldValuePairWithAggregation } from './use_fetch_field_value_pair_with_aggregation'; -import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { useGlobalTime } from '../../../common/containers/use_global_time'; jest.mock('@tanstack/react-query'); jest.mock('../../../common/lib/kibana'); -jest.mock('../../../common/hooks/use_selector'); -jest.mock('../../../common/containers/use_global_time'); const highlightedField = { name: 'field', values: ['values'], }; -const isActiveTimelines = true; const aggregationField = 'aggregationField'; describe('useFetchFieldValuePairWithAggregation', () => { @@ -39,8 +34,6 @@ describe('useFetchFieldValuePairWithAggregation', () => { data: { search: jest.fn() }, }, }); - jest.mocked(useDeepEqualSelector).mockReturnValue({ to: '', from: '' }); - (useGlobalTime as jest.Mock).mockReturnValue({ to: '', from: '' }); it('should return loading true while data is being fetched', () => { (useQuery as jest.Mock).mockReturnValue({ @@ -52,7 +45,6 @@ describe('useFetchFieldValuePairWithAggregation', () => { hookResult = renderHook(() => useFetchFieldValuePairWithAggregation({ highlightedField, - isActiveTimelines, aggregationField, }) ); @@ -72,7 +64,6 @@ describe('useFetchFieldValuePairWithAggregation', () => { hookResult = renderHook(() => useFetchFieldValuePairWithAggregation({ highlightedField, - isActiveTimelines, aggregationField, }) ); @@ -92,7 +83,6 @@ describe('useFetchFieldValuePairWithAggregation', () => { hookResult = renderHook(() => useFetchFieldValuePairWithAggregation({ highlightedField, - isActiveTimelines, aggregationField, }) ); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.ts b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.ts index 86c30e6008b36..16be9931db1f3 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.ts +++ b/x-pack/plugins/security_solution/public/flyout/shared/hooks/use_fetch_field_value_pair_with_aggregation.ts @@ -12,21 +12,16 @@ import { buildAggregationSearchRequest } from '../utils/build_requests'; import type { RawAggregatedDataResponse } from '../utils/fetch_data'; import { AGG_KEY, createFetchData } from '../utils/fetch_data'; import { useKibana } from '../../../common/lib/kibana'; -import { inputsSelectors } from '../../../common/store'; -import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { useGlobalTime } from '../../../common/containers/use_global_time'; const QUERY_KEY = 'useFetchFieldValuePairWithAggregation'; +const DEFAULT_FROM = 'now-30d'; +const DEFAULT_TO = 'now'; export interface UseFetchFieldValuePairWithAggregationParams { /** * The highlighted field name and values * */ highlightedField: { name: string; values: string[] }; - /** - * - */ - isActiveTimelines: boolean; /** * Field to aggregate value by */ @@ -55,7 +50,6 @@ export interface UseFetchFieldValuePairWithAggregationResult { */ export const useFetchFieldValuePairWithAggregation = ({ highlightedField, - isActiveTimelines, aggregationField, }: UseFetchFieldValuePairWithAggregationParams): UseFetchFieldValuePairWithAggregationResult => { const { @@ -64,12 +58,7 @@ export const useFetchFieldValuePairWithAggregation = ({ }, } = useKibana(); - const timelineTime = useDeepEqualSelector((state) => - inputsSelectors.timelineTimeRangeSelector(state) - ); - const globalTime = useGlobalTime(); - const { to, from } = isActiveTimelines ? timelineTime : globalTime; - + const { from, to } = { from: DEFAULT_FROM, to: DEFAULT_TO }; const { name, values } = highlightedField; const searchRequest = buildSearchRequest(name, values, from, to, aggregationField); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/components/endpoint_event_collection_preset.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/components/endpoint_event_collection_preset.tsx new file mode 100644 index 0000000000000..b07ab081a638f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/components/endpoint_event_collection_preset.tsx @@ -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 React, { memo, useEffect, useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiRadio, EuiSpacer } from '@elastic/eui'; +import type { + NewPackagePolicy, + PackagePolicyCreateExtensionComponentProps, +} from '@kbn/fleet-plugin/public'; +import type { EndpointPreset } from '../constants'; +import { ENDPOINT_INTEGRATION_CONFIG_KEY } from '../constants'; +import { HelpTextWithPadding } from './help_text_with_padding'; +import { DATA_COLLECTION, DATA_COLLECTION_HELP_TEXT } from '../translations'; +import { useGetProtectionsUnavailableComponent } from '../../../policy_settings_form/hooks/use_get_protections_unavailable_component'; + +const NOOP = () => {}; + +type EndpointEventCollectionPresetProps = PackagePolicyCreateExtensionComponentProps; + +/** + * Display ONLY the event collection option on the screen along with the upselling message + */ +export const EndpointEventCollectionPreset = memo( + ({ onChange, newPolicy }) => { + const UpsellToIncludePolicyProtections = useGetProtectionsUnavailableComponent(); + const preset: EndpointPreset = 'DataCollection'; + const policyInputs: NewPackagePolicy['inputs'] = useMemo(() => { + return [ + { + enabled: true, + streams: [], + type: ENDPOINT_INTEGRATION_CONFIG_KEY, + config: { + _config: { + value: { + type: 'endpoint', + endpointConfig: { + preset, + }, + }, + }, + }, + }, + ]; + }, []); + + useEffect(() => { + const inputs = newPolicy.inputs; + + if (inputs.length === 0) { + onChange({ + isValid: false, + updatedPolicy: { + ...newPolicy, + name: '', + inputs: policyInputs, + }, + }); + return; + } + + if (inputs[0]?.config?._config.value.endpointConfig.preset !== preset) { + onChange({ + isValid: true, + updatedPolicy: { + ...newPolicy, + inputs: policyInputs, + }, + }); + } + }, [newPolicy, onChange, policyInputs]); + + return ( +
+ + {DATA_COLLECTION_HELP_TEXT}} + > + + + + + {DATA_COLLECTION} + + + + {UpsellToIncludePolicyProtections && ( + <> + + + + )} +
+ ); + } +); +EndpointEventCollectionPreset.displayName = 'EndpointEventCollectionPreset'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/components/help_text_with_padding.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/components/help_text_with_padding.tsx new file mode 100644 index 0000000000000..3fd4e8d60dbca --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/components/help_text_with_padding.tsx @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import styled from 'styled-components'; + +export const HelpTextWithPadding = styled.div` + padding-left: ${(props) => props.theme.eui.euiSizeL}; +`; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/constants.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/constants.ts new file mode 100644 index 0000000000000..c5606bb41a05d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/constants.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { deepFreeze } from '@kbn/std'; +import { + DATA_COLLECTION, + EDR_COMPLETE, + EDR_ESSENTIAL, + EDR_NOTE, + NGAV, + NGAV_NOTE, +} from './translations'; + +export const ENDPOINT_INTEGRATION_CONFIG_KEY = 'ENDPOINT_INTEGRATION_CONFIG'; + +export const endpointPresetsMapping = deepFreeze({ + NGAV: { + label: NGAV, + note: NGAV_NOTE, + }, + EDREssential: { + label: EDR_ESSENTIAL, + note: EDR_NOTE, + }, + EDRComplete: { + label: EDR_COMPLETE, + note: EDR_NOTE, + }, + DataCollection: { + label: DATA_COLLECTION, + note: null, + }, +}); + +export type EndpointPreset = keyof typeof endpointPresetsMapping; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.test.tsx index d1875b4492a04..cfbdcfd12c26e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.test.tsx @@ -17,6 +17,7 @@ import type { import type { AppContextTestRender } from '../../../../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../../../../common/mock/endpoint'; import { licenseService } from '../../../../../../common/hooks/use_license'; +import type { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/public'; jest.mock('../../../../../../common/lib/kibana'); jest.mock('../../../../../../common/hooks/use_license', () => { @@ -59,8 +60,7 @@ const getMockNewPackage = (): NewPackagePolicy => { }; describe('Onboarding Component new section', () => { - let render: () => ReturnType; - let renderResult: ReturnType; + let renderResult: ReturnType; let mockedContext: AppContextTestRender; beforeEach(() => { @@ -159,4 +159,107 @@ describe('Onboarding Component new section', () => { }); }); }); + + describe('when policy protections are not available', () => { + let newPolicy: NewPackagePolicy; + let onChange: PackagePolicyCreateExtensionComponentProps['onChange']; + let render: () => ReturnType; + + beforeEach(() => { + mockedContext.startServices.upselling.registerSections({ + endpointPolicyProtections: () =>
{'pay up!'}
, + }); + newPolicy = getMockNewPackage(); + onChange = jest.fn(); + render = () => { + renderResult = mockedContext.render( + + ); + return renderResult; + }; + }); + + it('should render expected preset for endpoint', () => { + const { getByTestId } = render(); + + expect(getByTestId('endpointDataCollectionOnlyPreset')).toHaveTextContent( + 'Data Collection' + + 'Augment your existing anti-virus solution with advanced data collection and detection' + + 'pay up!' + ); + }); + + it('should set the correct value for preset in policy', () => { + render(); + + expect(onChange).toHaveBeenLastCalledWith({ + isValid: true, + updatedPolicy: { + enabled: true, + id: 'someid', + inputs: [ + { + config: { + _config: { + value: { + endpointConfig: { + preset: 'DataCollection', + }, + type: 'endpoint', + }, + }, + }, + enabled: true, + streams: [], + type: 'ENDPOINT_INTEGRATION_CONFIG', + }, + ], + name: 'someName', + namespace: 'someNamespace', + policy_id: 'somePolicyid', + }, + }); + }); + + it('should still be able to select cloud configuration', () => { + render(); + userEvent.selectOptions(screen.getByTestId('selectIntegrationTypeId'), ['cloud']); + + expect(onChange).toHaveBeenLastCalledWith({ + isValid: true, + updatedPolicy: { + enabled: true, + id: 'someid', + inputs: [ + { + config: { + _config: { + value: { + eventFilters: { + nonInteractiveSession: true, + }, + type: 'cloud', + }, + }, + }, + enabled: true, + streams: [ + { + data_stream: { + dataset: 'someDataset', + type: 'someType', + }, + enabled: true, + }, + ], + type: 'someType', + }, + ], + name: 'someName', + namespace: 'someNamespace', + policy_id: 'somePolicyid', + }, + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx index b67e1d6d0dca2..d9349eb6d6f5c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx @@ -5,70 +5,47 @@ * 2.0. */ -import React, { memo, useState, useEffect, useCallback } from 'react'; +import React, { memo, useCallback, useEffect, useState } from 'react'; import { + EuiCallOut, + EuiCode, EuiForm, + EuiFormRow, + EuiLink, EuiRadio, EuiSelect, + EuiSpacer, EuiText, EuiTitle, - EuiSpacer, - EuiFormRow, - EuiCallOut, - EuiLink, - EuiCode, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import styled from 'styled-components'; import type { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/public'; +import type { EndpointPreset } from './constants'; +import { ENDPOINT_INTEGRATION_CONFIG_KEY, endpointPresetsMapping } from './constants'; +import { HelpTextWithPadding } from './components/help_text_with_padding'; +import { EndpointEventCollectionPreset } from './components/endpoint_event_collection_preset'; import { useLicense } from '../../../../../../common/hooks/use_license'; import { ALL_EVENTS, CLOUD_SECURITY, - EDR_COMPLETE, - NGAV, - EDR_ESSENTIAL, + DATA_COLLECTION_HELP_TEXT, ENDPOINT, INTERACTIVE_ONLY, - NGAV_NOTE, - EDR_NOTE, - DATA_COLLECTION, } from './translations'; +import { useGetProtectionsUnavailableComponent } from '../../policy_settings_form/hooks/use_get_protections_unavailable_component'; const PREFIX = 'endpoint_policy_create_extension'; -const ENDPOINT_INTEGRATION_CONFIG_KEY = 'ENDPOINT_INTEGRATION_CONFIG'; - const environmentMapping = { cloud: CLOUD_SECURITY, endpoint: ENDPOINT, }; -const endpointPresetsMapping = { - NGAV: { - label: NGAV, - note: NGAV_NOTE, - }, - EDREssential: { - label: EDR_ESSENTIAL, - note: EDR_NOTE, - }, - EDRComplete: { - label: EDR_COMPLETE, - note: EDR_NOTE, - }, - DataCollection: { - label: DATA_COLLECTION, - note: null, - }, -}; - const cloudEventMapping = { INTERACTIVE_ONLY, ALL_EVENTS, }; -type EndpointPreset = keyof typeof endpointPresetsMapping; type CloudEvent = keyof typeof cloudEventMapping; type Environment = keyof typeof environmentMapping; @@ -77,10 +54,6 @@ const environmentOptions: Array<{ value: Environment; text: string }> = [ { value: 'cloud', text: CLOUD_SECURITY }, ]; -const HelpTextWithPadding = styled.div` - padding-left: ${(props) => props.theme.eui.euiSizeL}; -`; - /** * Exports Endpoint-specific package policy instructions * for use in the Ingest app create / edit package policy @@ -89,6 +62,7 @@ export const EndpointPolicyCreateExtension = memo { const isPlatinumPlus = useLicense().isPlatinumPlus(); const isEnterprise = useLicense().isEnterprise(); + const showEndpointEventCollectionOnlyPreset = Boolean(useGetProtectionsUnavailableComponent()); const [endpointPreset, setEndpointPreset] = useState('EDRComplete'); const [selectedCloudEvent, setSelectedCloudEvent] = useState('INTERACTIVE_ONLY'); @@ -105,63 +79,77 @@ export const EndpointPolicyCreateExtension = memo { - if (newPolicy.inputs.length === 0) { - onChange({ - isValid: false, - updatedPolicy: { - ...newPolicy, - name: '', - inputs: [ - { - enabled: true, - streams: [], - type: ENDPOINT_INTEGRATION_CONFIG_KEY, - config: { - _config: { - value: { - type: 'endpoint', - endpointConfig: { - preset: 'NGAV', + // When ONLY Data collection is allowed, the updates to the policy are handled by the + // EndpointEventCollectionPreset component + if ( + !showEndpointEventCollectionOnlyPreset || + (showEndpointEventCollectionOnlyPreset && selectedEnvironment === 'cloud') + ) { + if (newPolicy.inputs.length === 0) { + onChange({ + isValid: false, + updatedPolicy: { + ...newPolicy, + name: '', + inputs: [ + { + enabled: true, + streams: [], + type: ENDPOINT_INTEGRATION_CONFIG_KEY, + config: { + _config: { + value: { + type: 'endpoint', + endpointConfig: { + preset: 'NGAV', + }, }, }, }, }, - }, - ], - }, - }); - } else { - onChange({ - isValid: true, - updatedPolicy: { - ...newPolicy, - inputs: [ - { - ...newPolicy.inputs[0], - config: { - _config: { - value: { - type: selectedEnvironment, - ...(selectedEnvironment === 'cloud' - ? { - eventFilters: { - nonInteractiveSession: selectedCloudEvent === 'INTERACTIVE_ONLY', - }, - } - : { - endpointConfig: { - preset: endpointPreset, - }, - }), + ], + }, + }); + } else { + onChange({ + isValid: true, + updatedPolicy: { + ...newPolicy, + inputs: [ + { + ...newPolicy.inputs[0], + config: { + _config: { + value: { + type: selectedEnvironment, + ...(selectedEnvironment === 'cloud' + ? { + eventFilters: { + nonInteractiveSession: selectedCloudEvent === 'INTERACTIVE_ONLY', + }, + } + : { + endpointConfig: { + preset: endpointPreset, + }, + }), + }, }, }, }, - }, - ], - }, - }); + ], + }, + }); + } } - }, [selectedEnvironment, selectedCloudEvent, endpointPreset, onChange, newPolicy]); + }, [ + selectedEnvironment, + selectedCloudEvent, + endpointPreset, + onChange, + newPolicy, + showEndpointEventCollectionOnlyPreset, + ]); const onChangeEnvironment = useCallback((e: React.ChangeEvent) => { setSelectedEnvironment(e?.target?.value as Environment); @@ -243,94 +231,93 @@ export const EndpointPolicyCreateExtension = memo + {selectedEnvironment === 'endpoint' ? ( - <> - - - - - } - > - - - - - - - } - > - - - - - - - } - > - - - - - - - } - > - - - {showNote && ( - <> - - - -

- {endpointPresetsMapping[endpointPreset].note}{' '} - - - - ), - }} - /> -

-
-
- - )} - + !showEndpointEventCollectionOnlyPreset ? ( + <> + + {DATA_COLLECTION_HELP_TEXT}} + > + + + + + + + } + > + + + + + + + } + > + + + + + + + } + > + + + + {showNote && ( + <> + + + +

+ {endpointPresetsMapping[endpointPreset].note}{' '} + + + + ), + }} + /> +

+
+
+ + )} + + ) : ( + + ) ) : ( <> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts index 3772924fafd1d..e79e06d55e65b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts @@ -49,6 +49,14 @@ export const DATA_COLLECTION = i18n.translate( } ); +export const DATA_COLLECTION_HELP_TEXT = i18n.translate( + 'xpack.securitySolution.createPackagePolicy.stepConfigure.packagePolicyTypeEndpointDataCollection', + { + defaultMessage: + 'Augment your existing anti-virus solution with advanced data collection and detection', + } +); + export const ENDPOINT = i18n.translate( 'xpack.securitySolution.createPackagePolicy.stepConfigure.endpointDropdownOption', { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension.tsx index 840abd074eb5c..5955661c82864 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension.tsx @@ -7,12 +7,26 @@ import { lazy } from 'react'; import type { PackagePolicyCreateExtensionComponent } from '@kbn/fleet-plugin/public'; +import type { FleetUiExtensionGetterOptions } from './types'; + +export const getLazyEndpointPolicyCreateExtension = ({ + coreStart, + depsStart, + services, +}: FleetUiExtensionGetterOptions) => { + return lazy(async () => { + const [{ withSecurityContext }, { EndpointPolicyCreateExtension }] = await Promise.all([ + import('./components/with_security_context/with_security_context'), + import('./endpoint_policy_create_extension'), + ]); -export const LazyEndpointPolicyCreateExtension = lazy( - async () => { - const { EndpointPolicyCreateExtension } = await import('./endpoint_policy_create_extension'); return { - default: EndpointPolicyCreateExtension, + default: withSecurityContext({ + coreStart, + depsStart, + services, + WrappedComponent: EndpointPolicyCreateExtension, + }), }; - } -); + }); +}; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index f619dead10399..2ebe399c64f6a 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -44,7 +44,7 @@ import type { SecuritySolutionUiConfigType } from './common/types'; import { ExperimentalFeaturesService } from './common/experimental_features_service'; import { getLazyEndpointPolicyEditExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension'; -import { LazyEndpointPolicyCreateExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension'; +import { getLazyEndpointPolicyCreateExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension'; import { LazyEndpointPolicyCreateMultiStepExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_multi_step_extension'; import { getLazyEndpointPackageCustomExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_package_custom_extension'; import { getLazyEndpointPolicyResponseExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_response_extension'; @@ -306,7 +306,7 @@ export class Plugin implements IPlugin = ({ data-test-subj="timeline-flyout" css={css` min-width: 150px; - height: calc(100% - 96px); - top: 96px; + height: 100%; + top: 0; + left: 0; background: ${useEuiBackgroundColor('plain')}; position: fixed; width: 100%; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/use_sub_action.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/use_sub_action.tsx new file mode 100644 index 0000000000000..44f9099a5b816 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/use_sub_action.tsx @@ -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 { executeAction } from '@kbn/triggers-actions-ui-plugin/public'; +import { useQuery } from '@tanstack/react-query'; +import { useKibana } from '../../../../../common/lib/kibana/kibana_react'; + +export interface UseSubActionParams

{ + connectorId: string; + subAction: string; + subActionParams?: P; + disabled?: boolean; +} + +export const useSubAction = ({ + connectorId, + subAction, + subActionParams, + disabled = false, + ...rest +}: UseSubActionParams

) => { + const { http } = useKibana().services; + + return useQuery({ + queryKey: ['useSubAction', connectorId, subAction, subActionParams], + queryFn: ({ signal }) => + executeAction({ + id: connectorId, + params: { + subAction, + subActionParams, + }, + http, + signal, + }), + enabled: !disabled && !!connectorId && !!subAction, + ...rest, + }); +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/use_sub_action_mutation.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/use_sub_action_mutation.tsx new file mode 100644 index 0000000000000..78c48ccca1491 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/use_sub_action_mutation.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { executeAction } from '@kbn/triggers-actions-ui-plugin/public'; +import { useMutation } from '@tanstack/react-query'; +import { useKibana } from '../../../../../common/lib/kibana/kibana_react'; + +export interface UseSubActionParams

{ + connectorId: string; + subAction: string; + subActionParams?: P; + disabled?: boolean; +} + +export const useSubActionMutation = ({ + connectorId, + subAction, + subActionParams, + disabled = false, +}: UseSubActionParams

) => { + const { http } = useKibana().services; + + return useMutation({ + mutationFn: () => + executeAction({ + id: connectorId, + params: { + subAction, + subActionParams, + }, + http, + }), + }); +}; diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts index 351b38f91f47e..cf2f3897bd5ab 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts @@ -13,6 +13,9 @@ import pMap from 'p-map'; import { ToolingLog } from '@kbn/tooling-log'; import { withProcRunner } from '@kbn/dev-proc-runner'; import cypress from 'cypress'; +import { findChangedFiles } from 'find-cypress-specs'; +import minimatch from 'minimatch'; +import path from 'path'; import { EsVersion, @@ -68,13 +71,39 @@ const retrieveIntegrations = ( export const cli = () => { run( async () => { - const { argv } = yargs(process.argv.slice(2)); + const { argv } = yargs(process.argv.slice(2)).coerce('env', (arg: string) => + arg.split(',').reduce((acc, curr) => { + const [key, value] = curr.split('='); + if (key === 'burn') { + acc[key] = parseInt(value, 10); + } else { + acc[key] = value; + } + return acc; + }, {} as Record) + ); const isOpen = argv._[0] === 'open'; const cypressConfigFilePath = require.resolve(`../../${argv.configFile}`) as string; const cypressConfigFile = await import(require.resolve(`../../${argv.configFile}`)); const spec: string | undefined = argv?.spec as string; - const files = retrieveIntegrations(spec ? [spec] : cypressConfigFile?.e2e?.specPattern); + let files = retrieveIntegrations(spec ? [spec] : cypressConfigFile?.e2e?.specPattern); + + if (argv.changedSpecsOnly) { + const basePath = process.cwd().split('kibana/')[1]; + files = findChangedFiles('main', false) + .filter( + minimatch.filter(path.join(basePath, cypressConfigFile?.e2e?.specPattern), { + matchBase: true, + }) + ) + .map((filePath: string) => filePath.replace(basePath, '.')); + + if (!files?.length) { + // eslint-disable-next-line no-process-exit + return process.exit(0); + } + } if (!files?.length) { throw new Error('No files found'); @@ -323,8 +352,8 @@ ${JSON.stringify(config.getAll(), null, 2)} type: 'elasticsearch' | 'kibana' | 'fleetserver', withAuth: boolean = false ): string => { - const getKeyPath = (path: string = ''): string => { - return `servers.${type}${path ? `.${path}` : ''}`; + const getKeyPath = (keyPath: string = ''): string => { + return `servers.${type}${keyPath ? `.${keyPath}` : ''}`; }; if (!config.get(getKeyPath())) { @@ -361,7 +390,7 @@ ${JSON.stringify(config.getAll(), null, 2)} ...ftrEnv, // NOTE: - // ELASTICSEARCH_URL needs to be crated here with auth because SIEM cypress setup depends on it. At some + // ELASTICSEARCH_URL needs to be created here with auth because SIEM cypress setup depends on it. At some // points we should probably try to refactor that code to use `ELASTICSEARCH_URL_WITH_AUTH` instead ELASTICSEARCH_URL: ftrEnv.ELASTICSEARCH_URL ?? createUrlFromFtrConfig('elasticsearch', true), @@ -377,6 +406,8 @@ ${JSON.stringify(config.getAll(), null, 2)} KIBANA_URL_WITH_AUTH: createUrlFromFtrConfig('kibana', true), KIBANA_USERNAME: config.get('servers.kibana.username'), KIBANA_PASSWORD: config.get('servers.kibana.password'), + + ...argv.env, }; log.info(` @@ -407,6 +438,7 @@ ${JSON.stringify(cyCustomEnv, null, 2)} configFile: cypressConfigFilePath, reporter: argv.reporter as string, reporterOptions: argv.reporterOptions, + headed: argv.headed as boolean, config: { e2e: { baseUrl, diff --git a/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts b/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts index 5384e68c5945f..a2bf02c59b306 100644 --- a/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts +++ b/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts @@ -13,6 +13,10 @@ import { createUICapabilities as createCasesUICapabilities, getApiTags as getCasesApiTags, } from '@kbn/cases-plugin/common'; +import { + CASES_CONNECTORS_CAPABILITY, + GET_CONNECTORS_CONFIGURE_API_TAG, +} from '@kbn/cases-plugin/common/constants'; import type { AppFeaturesCasesConfig, BaseKibanaFeatureConfig } from './types'; import { APP_ID, CASES_FEATURE_ID } from '../../../common/constants'; import { CasesSubFeatureId } from './security_cases_kibana_sub_features'; @@ -21,48 +25,66 @@ import { AppFeatureCasesKey } from '../../../common/types/app_features'; const casesCapabilities = createCasesUICapabilities(); const casesApiTags = getCasesApiTags(APP_ID); -export const getCasesBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({ - id: CASES_FEATURE_ID, - name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionCaseTitle', { - defaultMessage: 'Cases', - }), - order: 1100, - category: DEFAULT_APP_CATEGORIES.security, - app: [CASES_FEATURE_ID, 'kibana'], - catalogue: [APP_ID], - cases: [APP_ID], - privileges: { - all: { - api: casesApiTags.all, - app: [CASES_FEATURE_ID, 'kibana'], - catalogue: [APP_ID], - cases: { - create: [APP_ID], - read: [APP_ID], - update: [APP_ID], - push: [APP_ID], - }, - savedObject: { - all: [...filesSavedObjectTypes], - read: [...filesSavedObjectTypes], - }, - ui: casesCapabilities.all, - }, - read: { - api: casesApiTags.read, - app: [CASES_FEATURE_ID, 'kibana'], - catalogue: [APP_ID], - cases: { - read: [APP_ID], +export const getCasesBaseKibanaFeature = (): BaseKibanaFeatureConfig => { + // On SecuritySolution essentials cases does not have the connector feature + const casesAllUICapabilities = casesCapabilities.all.filter( + (capability) => capability !== CASES_CONNECTORS_CAPABILITY + ); + + const casesReadUICapabilities = casesCapabilities.read.filter( + (capability) => capability !== CASES_CONNECTORS_CAPABILITY + ); + + const casesAllAPICapabilities = casesApiTags.all.filter( + (capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG + ); + + const casesReadAPICapabilities = casesApiTags.read.filter( + (capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG + ); + + return { + id: CASES_FEATURE_ID, + name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionCaseTitle', { + defaultMessage: 'Cases', + }), + order: 1100, + category: DEFAULT_APP_CATEGORIES.security, + app: [CASES_FEATURE_ID, 'kibana'], + catalogue: [APP_ID], + cases: [APP_ID], + privileges: { + all: { + api: casesAllAPICapabilities, + app: [CASES_FEATURE_ID, 'kibana'], + catalogue: [APP_ID], + cases: { + create: [APP_ID], + read: [APP_ID], + update: [APP_ID], + }, + savedObject: { + all: [...filesSavedObjectTypes], + read: [...filesSavedObjectTypes], + }, + ui: casesAllUICapabilities, }, - savedObject: { - all: [], - read: [...filesSavedObjectTypes], + read: { + api: casesReadAPICapabilities, + app: [CASES_FEATURE_ID, 'kibana'], + catalogue: [APP_ID], + cases: { + read: [APP_ID], + }, + savedObject: { + all: [], + read: [...filesSavedObjectTypes], + }, + ui: casesReadUICapabilities, }, - ui: casesCapabilities.read, }, - }, -}); + }; +}; export const getCasesBaseKibanaSubFeatureIds = (): CasesSubFeatureId[] => [ CasesSubFeatureId.deleteCases, @@ -79,6 +101,18 @@ export const getCasesBaseKibanaSubFeatureIds = (): CasesSubFeatureId[] => [ */ export const getCasesAppFeaturesConfig = (): AppFeaturesCasesConfig => ({ [AppFeatureCasesKey.casesConnectors]: { - // TODO: Add cases connector configuration privileges + privileges: { + all: { + api: [GET_CONNECTORS_CONFIGURE_API_TAG], // Add cases connector get connectors API privileges + ui: [CASES_CONNECTORS_CAPABILITY], // Add cases connector UI privileges + cases: { + push: [APP_ID], // Add cases connector push privileges + }, + }, + read: { + api: [GET_CONNECTORS_CONFIGURE_API_TAG], // Add cases connector get connectors API privileges + ui: [CASES_CONNECTORS_CAPABILITY], // Add cases connector UI privileges + }, + }, }, }); diff --git a/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts b/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts index 7e0f8bfdaa4ce..356e4c1996e24 100644 --- a/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts +++ b/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts @@ -6,7 +6,6 @@ */ import type { Logger } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; -import { schema } from '@kbn/config-schema'; import type { DashboardAttributes } from '@kbn/dashboard-plugin/common'; import { transformError } from '@kbn/securitysolution-es-utils'; @@ -15,10 +14,8 @@ import type { SetupPlugins } from '../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { buildSiemResponse } from '../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../timeline/utils/common'; - -const getDashboardsParamsSchema = schema.object({ - tagIds: schema.arrayOf(schema.string()), -}); +import { getDashboardsRequest } from '../../../../common/api/tags'; +import { buildRouteValidationWithExcess } from '../../../utils/build_validation/route_validation'; export const getDashboardsByTagsRoute = ( router: SecuritySolutionPluginRouter, @@ -28,7 +25,7 @@ export const getDashboardsByTagsRoute = ( router.post( { path: INTERNAL_DASHBOARDS_URL, - validate: { body: getDashboardsParamsSchema }, + validate: { body: buildRouteValidationWithExcess(getDashboardsRequest) }, options: { tags: ['access:securitySolution'], }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts index 1d9d349dd0b5a..de4e9982c852d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts @@ -551,4 +551,72 @@ describe('stripNonEcsFields', () => { expect(removed).toEqual([]); }); }); + + // geo_point is too complex so we going to skip its validation + describe('geo_point field', () => { + it('should not strip invalid geo_point field', () => { + const { result, removed } = stripNonEcsFields({ + 'client.location.geo': 'invalid geo_point', + }); + + expect(result).toEqual({ + 'client.location.geo': 'invalid geo_point', + }); + expect(removed).toEqual([]); + }); + + it('should not strip valid geo_point fields', () => { + expect( + stripNonEcsFields({ + 'client.geo.location': [0, 90], + }).result + ).toEqual({ + 'client.geo.location': [0, 90], + }); + + expect( + stripNonEcsFields({ + 'client.geo.location': { + type: 'Point', + coordinates: [-88.34, 20.12], + }, + }).result + ).toEqual({ + 'client.geo.location': { + type: 'Point', + coordinates: [-88.34, 20.12], + }, + }); + + expect( + stripNonEcsFields({ + 'client.geo.location': 'POINT (-71.34 41.12)', + }).result + ).toEqual({ + 'client.geo.location': 'POINT (-71.34 41.12)', + }); + + expect( + stripNonEcsFields({ + client: { + geo: { + location: { + lat: 41.12, + lon: -71.34, + }, + }, + }, + }).result + ).toEqual({ + client: { + geo: { + location: { + lat: 41.12, + lon: -71.34, + }, + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts index 975b2b643a4e7..62e5c9211c1be 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts @@ -57,10 +57,11 @@ const ecsObjectFields = getEcsObjectFields(); /** * checks if path is a valid Ecs object type (object or flattened) + * geo_point also can be object */ const getIsEcsFieldObject = (path: string) => { const ecsField = ecsFieldMap[path as keyof typeof ecsFieldMap]; - return ['object', 'flattened'].includes(ecsField?.type) || ecsObjectFields[path]; + return ['object', 'flattened', 'geo_point'].includes(ecsField?.type) || ecsObjectFields[path]; }; /** @@ -117,6 +118,11 @@ const computeIsEcsCompliant = (value: SourceField, path: string) => { const ecsField = ecsFieldMap[path as keyof typeof ecsFieldMap]; const isEcsFieldObject = getIsEcsFieldObject(path); + // do not validate geo_point, since it's very complex type that can be string/array/object + if (ecsField?.type === 'geo_point') { + return true; + } + // validate if value is a long type if (ecsField?.type === 'long') { return isValidLongType(value); diff --git a/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts b/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts index 4eaed27813214..6ddc14d64d53f 100644 --- a/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts +++ b/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts @@ -6,22 +6,17 @@ */ import type { Logger } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; -import { schema } from '@kbn/config-schema'; import { transformError } from '@kbn/securitysolution-es-utils'; +import { createTagRequest } from '../../../../common/api/tags'; import { INTERNAL_TAGS_URL } from '../../../../common/constants'; import type { SetupPlugins } from '../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../types'; +import { buildRouteValidationWithExcess } from '../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../timeline/utils/common'; import { createTag } from '../saved_objects'; -const createTagBodySchema = schema.object({ - name: schema.string(), - description: schema.string(), - color: schema.maybe(schema.string()), -}); - export const createTagRoute = ( router: SecuritySolutionPluginRouter, logger: Logger, @@ -30,7 +25,7 @@ export const createTagRoute = ( router.put( { path: INTERNAL_TAGS_URL, - validate: { body: createTagBodySchema }, + validate: { body: buildRouteValidationWithExcess(createTagRequest) }, options: { tags: ['access:securitySolution'], }, diff --git a/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts b/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts index 05d20d90e09b2..a88578365f037 100644 --- a/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts +++ b/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts @@ -6,20 +6,17 @@ */ import type { Logger } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; -import { schema } from '@kbn/config-schema'; import { transformError } from '@kbn/securitysolution-es-utils'; +import { getTagsByNameRequest } from '../../../../common/api/tags'; import { INTERNAL_TAGS_URL } from '../../../../common/constants'; import type { SetupPlugins } from '../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../types'; +import { buildRouteValidationWithExcess } from '../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../timeline/utils/common'; import { findTagsByName } from '../saved_objects'; -const getTagsParamsSchema = schema.object({ - name: schema.string(), -}); - export const getTagsByNameRoute = ( router: SecuritySolutionPluginRouter, logger: Logger, @@ -28,7 +25,7 @@ export const getTagsByNameRoute = ( router.get( { path: INTERNAL_TAGS_URL, - validate: { query: getTagsParamsSchema }, + validate: { query: buildRouteValidationWithExcess(getTagsByNameRequest) }, options: { tags: ['access:securitySolution'], }, diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts b/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts index d95afe1bdb2b1..7192a1337fde4 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts @@ -12,4 +12,7 @@ export const getStartedStorage = { toggleActiveProductsInStorage: jest.fn(() => []), addFinishedStepToStorage: jest.fn(), removeFinishedStepFromStorage: jest.fn(), + addExpandedCardStepToStorage: jest.fn(), + removeExpandedCardStepFromStorage: jest.fn(), + getAllExpandedCardStepsFromStorage: jest.fn(() => ({})), }; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/badge.ts b/x-pack/plugins/security_solution_serverless/public/get_started/badge.ts new file mode 100644 index 0000000000000..1743259fd7eaa --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/badge.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ProductLine } from '../../common/product'; +import { PRODUCT_BADGE_ANALYTICS, PRODUCT_BADGE_CLOUD, PRODUCT_BADGE_EDR } from './translations'; +import type { Badge } from './types'; +import { BadgeId } from './types'; + +export const analyticsBadge: Badge = { + id: BadgeId.analytics, + name: PRODUCT_BADGE_ANALYTICS, +}; + +export const cloudBadge: Badge = { + id: BadgeId.cloud, + name: PRODUCT_BADGE_CLOUD, +}; + +export const edrBadge: Badge = { + id: BadgeId.edr, + name: PRODUCT_BADGE_EDR, +}; + +const productBadges: Record = { + [ProductLine.security]: analyticsBadge, + [ProductLine.cloud]: cloudBadge, + [ProductLine.endpoint]: edrBadge, +}; + +export const getProductBadges = (productLineRequired?: ProductLine[] | undefined): Badge[] => + (productLineRequired ?? [ProductLine.security, ProductLine.cloud, ProductLine.endpoint]).map( + (product) => productBadges[product] + ); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/badges.test.ts b/x-pack/plugins/security_solution_serverless/public/get_started/badges.test.ts new file mode 100644 index 0000000000000..95d7934a65027 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/badges.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 { ProductLine } from '../../common/product'; +import { analyticsBadge, cloudBadge, edrBadge, getProductBadges } from './badge'; + +describe('getProductBadges', () => { + test('should return all badges if no productLineRequired is passed', () => { + expect(getProductBadges()).toEqual([analyticsBadge, cloudBadge, edrBadge]); + }); + + test('should return only the badges for the productLineRequired passed', () => { + expect(getProductBadges([ProductLine.cloud])).toEqual([cloudBadge]); + }); +}); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx index c132eb72bb359..fdaa493b88e86 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx @@ -5,33 +5,48 @@ * 2.0. */ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; import { CardItem } from './card_item'; -import type { CardId, StepId } from './types'; +import type { CardId, ExpandedCardSteps, StepId } from './types'; import { GetSetUpCardId, IntroductionSteps, SectionId } from './types'; import type { EuiThemeComputed } from '@elastic/eui'; +import { introductionSteps } from './sections'; +import { ProductLine } from '../../common/product'; jest.mock('./card_step'); describe('CardItemComponent', () => { const finishedSteps = {} as Record>; - + const onCardStepClicked = jest.fn(); const onStepClicked = jest.fn(); + const onStepButtonClicked = jest.fn(); + const expandedCardSteps = { + [GetSetUpCardId.introduction]: { + isExpanded: false, + expandedSteps: [] as StepId[], + }, + } as ExpandedCardSteps; + const mockEuiTheme = { size: { xxs: '4px' }, base: 16 } as EuiThemeComputed; it('should render card', () => { const { getByText, queryByText } = render( step.id)} cardId={GetSetUpCardId.introduction} - sectionId={SectionId.getSetUp} + expandedCardSteps={expandedCardSteps} euiTheme={mockEuiTheme} + finishedSteps={finishedSteps} + onCardClicked={onCardStepClicked} + onStepButtonClicked={onStepButtonClicked} + onStepClicked={onStepClicked} + sectionId={SectionId.getSetUp} shadow="" stepsLeft={1} timeInMins={30} - onStepClicked={onStepClicked} - finishedSteps={finishedSteps} /> ); - const cardTitle = getByText('introduction'); + const cardTitle = getByText('Introduction'); expect(cardTitle).toBeInTheDocument(); const step = getByText('1 step left'); @@ -44,25 +59,53 @@ describe('CardItemComponent', () => { expect(step1).not.toBeInTheDocument(); }); - it('should not render steps left information when all steps done', () => { + it('should not render card when no active steps', () => { + const { queryByText } = render( + + ); + + const cardTitle = queryByText('Introduction'); + expect(cardTitle).not.toBeInTheDocument(); + }); + + it('should not render steps left information when all steps are done', () => { const mockFinishedSteps = { - [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.getToKnowElasticSecurity]), } as Record>; const { getByText, queryByText } = render( step.id)} cardId={GetSetUpCardId.introduction} sectionId={SectionId.getSetUp} + expandedCardSteps={expandedCardSteps} euiTheme={mockEuiTheme} shadow="" stepsLeft={0} timeInMins={0} + onCardClicked={onCardStepClicked} onStepClicked={onStepClicked} + onStepButtonClicked={onStepButtonClicked} finishedSteps={mockFinishedSteps} /> ); - const cardTitle = getByText('introduction'); + const cardTitle = getByText('Introduction'); expect(cardTitle).toBeInTheDocument(); const step = queryByText('1 step left'); @@ -71,4 +114,34 @@ describe('CardItemComponent', () => { const time = queryByText('• About 30 mins'); expect(time).not.toBeInTheDocument(); }); + + it('should toggle step expansion on click', () => { + const testCardTitle = 'Introduction'; + const { getByText } = render( + step.id)} + expandedCardSteps={expandedCardSteps} + cardId={GetSetUpCardId.introduction} + sectionId={SectionId.getSetUp} + euiTheme={mockEuiTheme} + shadow="" + stepsLeft={0} + timeInMins={0} + onCardClicked={onCardStepClicked} + onStepClicked={onStepClicked} + onStepButtonClicked={onStepButtonClicked} + finishedSteps={finishedSteps} + /> + ); + + const stepTitle = getByText(testCardTitle); + fireEvent.click(stepTitle); + + expect(onCardStepClicked).toHaveBeenCalledTimes(1); + expect(onCardStepClicked).toHaveBeenCalledWith({ + cardId: GetSetUpCardId.introduction, + isExpanded: true, + }); + }); }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx index 2dbd6da34d992..472b24ba6b5e2 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx @@ -7,43 +7,67 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPanel, EuiText, EuiTitle } from '@elastic/eui'; import type { EuiThemeComputed } from '@elastic/eui'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { css } from '@emotion/react'; -import type { CardId, SectionId, StepId } from './types'; +import type { + CardId, + ExpandedCardSteps, + OnCardClicked, + OnStepButtonClicked, + OnStepClicked, + SectionId, + StepId, +} from './types'; import * as i18n from './translations'; import { CardStep } from './card_step'; -import { getSections } from './sections'; +import { getCard } from './helpers'; +import type { ProductLine } from '../../common/product'; const CardItemComponent: React.FC<{ + activeProducts: Set; + activeStepIds: StepId[] | undefined; + cardId: CardId; euiTheme: EuiThemeComputed; + expandedCardSteps: ExpandedCardSteps; + finishedSteps: Record>; + onCardClicked: OnCardClicked; + onStepButtonClicked: OnStepButtonClicked; + onStepClicked: OnStepClicked; + sectionId: SectionId; shadow?: string; stepsLeft?: number; timeInMins?: number; - onStepClicked: (params: { stepId: StepId; cardId: CardId; sectionId: SectionId }) => void; - finishedSteps: Record>; - sectionId: SectionId; - cardId: CardId; }> = ({ - stepsLeft, - timeInMins, - shadow, + activeProducts, + activeStepIds, + cardId, euiTheme, - onStepClicked, + expandedCardSteps, finishedSteps, + onCardClicked, + onStepButtonClicked, + onStepClicked, sectionId, - cardId, + shadow, + stepsLeft, + timeInMins, }) => { - const section = getSections().find((s) => s.id === sectionId); - const cardItem = section?.cards?.find((c) => c.id === cardId); - const [expandCard, setExpandCard] = useState(false); + const cardItem = useMemo(() => getCard({ cardId, sectionId }), [cardId, sectionId]); + const expandCard = expandedCardSteps[cardId]?.isExpanded ?? false; + const expandedSteps = useMemo( + () => new Set(expandedCardSteps[cardId]?.expandedSteps ?? []), + [cardId, expandedCardSteps] + ); const toggleCard = useCallback( (e) => { e.preventDefault(); - setExpandCard(!expandCard); + const isExpanded = !expandCard; + onCardClicked({ cardId, isExpanded }); }, - [expandCard] + [cardId, expandCard, onCardClicked] ); - return cardItem ? ( + const hasActiveSteps = activeStepIds != null && activeStepIds.length > 0; + return cardItem && hasActiveSteps ? ( - {expandCard && cardItem.steps && ( + {expandCard && hasActiveSteps && ( - {cardItem.steps.map((step) => { + {[...activeStepIds].map((stepId) => { return ( ); })} diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.test.tsx index 9e72370f179e8..43c9b2182656c 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.test.tsx @@ -9,76 +9,79 @@ import { render, fireEvent } from '@testing-library/react'; import { CardStep } from './card_step'; import type { StepId } from './types'; import { GetSetUpCardId, IntroductionSteps, SectionId } from './types'; +import { ProductLine } from '../../common/product'; describe('CardStepComponent', () => { const step = { - id: IntroductionSteps.watchOverviewVideo, - title: 'Test Step', - badges: [ - { id: 'badge1', name: 'Badge 1' }, - { id: 'badge2', name: 'Badge 2' }, - ], - description: ['Description line 1', 'Description line 2'], - splitPanel:

{'Split Panel'}
, + id: IntroductionSteps.getToKnowElasticSecurity, }; const onStepClicked = jest.fn(); + const onStepButtonClicked = jest.fn(); + const expandedSteps = new Set([IntroductionSteps.getToKnowElasticSecurity]); const props = { - sectionId: SectionId.getSetUp, + activeProducts: new Set([ProductLine.security]), cardId: GetSetUpCardId.introduction, - step, + expandedSteps, + finishedStepsByCard: new Set(), + onStepButtonClicked, onStepClicked, - finishedStepsByCard: new Set() as Set, + sectionId: SectionId.getSetUp, + stepId: step.id, }; + const testStepTitle = 'Get to know Elastic Security'; it('should toggle step expansion on click', () => { const { getByText } = render(); - const stepTitle = getByText('Test Step'); + const stepTitle = getByText(testStepTitle); fireEvent.click(stepTitle); expect(onStepClicked).toHaveBeenCalledTimes(1); expect(onStepClicked).toHaveBeenCalledWith({ sectionId: SectionId.getSetUp, - stepId: IntroductionSteps.watchOverviewVideo, + stepId: IntroductionSteps.getToKnowElasticSecurity, cardId: GetSetUpCardId.introduction, + isExpanded: false, }); }); it('should render step title, badges, and description when expanded', () => { - const { getByText } = render(); + const { getByText, getByTestId } = render(); - const stepTitle = getByText('Test Step'); + const stepTitle = getByText(testStepTitle); fireEvent.click(stepTitle); - const badge1 = getByText('Badge 1'); - const badge2 = getByText('Badge 2'); + const badge1 = getByText('Analytics'); + const badge2 = getByText('Cloud'); + const badge3 = getByText('EDR'); expect(badge1).toBeInTheDocument(); expect(badge2).toBeInTheDocument(); + expect(badge3).toBeInTheDocument(); - const description1 = getByText('Description line 1'); - const description2 = getByText('Description line 2'); + const description1 = getByTestId(`${IntroductionSteps.getToKnowElasticSecurity}-description-0`); + const description2 = getByTestId(`${IntroductionSteps.getToKnowElasticSecurity}-description-1`); expect(description1).toBeInTheDocument(); expect(description2).toBeInTheDocument(); }); - it('should render split panel when expanded', () => { - const { getByText, queryByText } = render(); + it('should render expended steps', () => { + const { getByTestId } = render(); - const stepTitle = getByText('Test Step'); - fireEvent.click(stepTitle); - - const splitPanel = getByText('Split Panel'); + const splitPanel = getByTestId('split-panel'); expect(splitPanel).toBeInTheDocument(); + }); - fireEvent.click(stepTitle); + it('should render collapsed steps', () => { + const { queryByTestId } = render(); - expect(queryByText('Split Panel')).not.toBeInTheDocument(); + const splitPanel = queryByTestId('split-panel'); + expect(splitPanel).not.toBeInTheDocument(); }); it('should render check icon when stepId is in finishedStepsByCard', () => { - const finishedStepsByCard = new Set([IntroductionSteps.watchOverviewVideo]); + const finishedStepsByCard = new Set([IntroductionSteps.getToKnowElasticSecurity]); const { getByTestId } = render( diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.tsx index 137895b9bb078..50f1a0b16133f 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.tsx @@ -14,36 +14,72 @@ import { EuiSplitPanel, EuiSpacer, EuiText, + useEuiTheme, + EuiButtonEmpty, } from '@elastic/eui'; import { css } from '@emotion/react'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; -import type { CardId, SectionId, Step, StepId } from './types'; -import step from './images/step.svg'; +import type { CardId, OnStepButtonClicked, OnStepClicked, SectionId, StepId } from './types'; +import icon_step from './images/icon_step.svg'; +import icon_cross from './images/icon_cross.svg'; +import { UNDO_MARK_AS_DONE_TITLE, MARK_AS_DONE_TITLE } from './translations'; +import { getStepsByActiveProduct } from './helpers'; +import type { ProductLine } from '../../common/product'; +import { getProductBadges } from './badge'; const CardStepComponent: React.FC<{ - sectionId: SectionId; + activeProducts: Set; cardId: CardId; - step: Step; - onStepClicked: (params: { stepId: StepId; cardId: CardId; sectionId: SectionId }) => void; + expandedSteps: Set; finishedStepsByCard: Set; + onStepButtonClicked: OnStepButtonClicked; + onStepClicked: OnStepClicked; + sectionId: SectionId; + stepId: StepId; }> = ({ - sectionId, + activeProducts, cardId, - step: { id: stepId, title, badges, description, splitPanel }, - onStepClicked, + expandedSteps, finishedStepsByCard = new Set(), + onStepButtonClicked, + onStepClicked, + sectionId, + stepId, }) => { - const [expandStep, setExpandStep] = useState(false); + const { euiTheme } = useEuiTheme(); + + const expandStep = expandedSteps.has(stepId); + const steps = useMemo( + () => getStepsByActiveProduct({ activeProducts, cardId, sectionId }), + [activeProducts, cardId, sectionId] + ); + const { title, productLineRequired, description, splitPanel } = + steps?.find((step) => step.id === stepId) ?? {}; + + const badges = useMemo(() => getProductBadges(productLineRequired), [productLineRequired]); + const toggleStep = useCallback( (e) => { e.preventDefault(); - setExpandStep(!expandStep); - onStepClicked({ stepId, cardId, sectionId }); + const newState = !expandStep; + onStepClicked({ stepId, cardId, sectionId, isExpanded: newState }); }, [cardId, expandStep, onStepClicked, sectionId, stepId] ); + const isDone = finishedStepsByCard.has(stepId); + + const hasStepContent = description || splitPanel; + + const handleStepButtonClicked = useCallback( + (e) => { + e.preventDefault(); + onStepButtonClicked({ stepId, cardId, sectionId, undo: isDone ? true : false }); + }, + [cardId, isDone, onStepButtonClicked, sectionId, stepId] + ); + return ( - + - + - {title} + + {title} + {badges.map((badge) => ( {badge.name} @@ -76,31 +118,76 @@ const CardStepComponent: React.FC<{ align-items: end; `} > - +
+ + {isDone ? UNDO_MARK_AS_DONE_TITLE : MARK_AS_DONE_TITLE} + + +
- {expandStep && (description || splitPanel) && ( - - {description && ( - - - - {description?.map((desc, index) => ( -

- {desc} -

- ))} -
-
- )} - {splitPanel && {splitPanel}} -
+ {expandStep && hasStepContent && ( + <> + + + {description && ( + + + {description?.map((desc, index) => ( +

+ {desc} +

+ ))} +
+
+ )} + {splitPanel && ( + + {splitPanel} + + )} +
+ )}
); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx index 55550495e6e13..0a1d1e7693912 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { css } from '@emotion/react'; +import { NavigationProvider } from '@kbn/security-solution-navigation'; import { WelcomePanel } from './welcome_panel'; import { TogglePanel } from './toggle_panel'; import { @@ -20,6 +21,7 @@ import { import type { SecurityProductTypes } from '../../common/config'; import { ProductSwitch } from './product_switch'; import { useTogglePanel } from './use_toggle_panel'; +import { useKibana } from '../common/services'; const CONTENT_WIDTH = 1150; @@ -32,9 +34,19 @@ export const GetStartedComponent: React.FC = ({ productTypes }) const shadow = useEuiShadow('s'); const { onProductSwitchChanged, + onCardClicked, onStepClicked, - state: { activeProducts, activeCards, finishedSteps }, + onStepButtonClicked, + state: { + activeProducts, + activeSections, + finishedSteps, + totalActiveSteps, + totalStepsLeft, + expandedCardSteps, + }, } = useTogglePanel({ productTypes }); + const services = useKibana().services; return ( = ({ productTypes }) } > - + = ({ productTypes }) padding: 0 ${euiTheme.base * 2.25}px; `} > - + + + ); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts index 050d42fbec573..5e14bbab324dc 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts @@ -8,17 +8,19 @@ import { getCardTimeInMinutes, getCardStepsLeft, - isCardActive, - setupCards, - updateCard, + setupActiveSections, + updateActiveSections, + isStepActive, } from './helpers'; -import type { ActiveCards, Card, CardId, Section, StepId } from './types'; +import type { ActiveSections, Card, CardId, Section, Step, StepId } from './types'; import { - GetMoreFromElasticSecurityCardId, + ExploreSteps, + ConfigureSteps, GetSetUpCardId, IntroductionSteps, SectionId, } from './types'; + import * as sectionsConfigs from './sections'; import { ProductLine } from '../../common/product'; const mockSections = jest.spyOn(sectionsConfigs, 'getSections'); @@ -31,18 +33,23 @@ describe('getCardTimeInMinutes', () => { { id: 'step3', timeInMinutes: 15 }, ], } as unknown as Card; + const activeProducts = new Set([ProductLine.security, ProductLine.cloud]); + const activeSteps = card.steps?.filter((step) => isStepActive(step, activeProducts)); const stepsDone = new Set(['step1', 'step3']) as unknown as Set; - const timeInMinutes = getCardTimeInMinutes(card, stepsDone); + const timeInMinutes = getCardTimeInMinutes(activeSteps, stepsDone); expect(timeInMinutes).toEqual(45); }); it('should return 0 if the card is null or has no steps', () => { const card = {} as Card; + + const activeProducts = new Set([ProductLine.security, ProductLine.cloud]); + const activeSteps = card.steps?.filter((step) => isStepActive(step, activeProducts)); const stepsDone = new Set(['step1']) as unknown as Set; - const timeInMinutes = getCardTimeInMinutes(card, stepsDone); + const timeInMinutes = getCardTimeInMinutes(activeSteps, stepsDone); expect(timeInMinutes).toEqual(0); }); @@ -51,127 +58,121 @@ describe('getCardTimeInMinutes', () => { describe('getCardStepsLeft', () => { it('should calculate the number of steps left for a card correctly', () => { const card = { steps: ['step1', 'step2', 'step3'] } as unknown as Card; + const activeProducts = new Set([ProductLine.security, ProductLine.cloud]); + const activeSteps = card.steps?.filter((step) => isStepActive(step, activeProducts)); const stepsDone = new Set(['step1', 'step3']) as unknown as Set; - const stepsLeft = getCardStepsLeft(card, stepsDone); + const stepsLeft = getCardStepsLeft(activeSteps, stepsDone); expect(stepsLeft).toEqual(1); }); it('should return the total number of steps if the card is null or has no steps', () => { const card = {} as Card; + const activeProducts = new Set([ProductLine.security, ProductLine.cloud]); + const activeSteps = card.steps?.filter((step) => isStepActive(step, activeProducts)); const stepsDone = new Set() as unknown as Set; - const stepsLeft = getCardStepsLeft(card, stepsDone); + const stepsLeft = getCardStepsLeft(activeSteps, stepsDone); expect(stepsLeft).toEqual(0); }); }); -describe('isCardActive', () => { - it('should return true if the card is active based on the active products', () => { - const card = { productLineRequired: [ProductLine.security, ProductLine.cloud] } as Card; - const activeProducts = new Set([ProductLine.security]); +describe('isStepActive', () => { + it('should return true if the step is active based on the active products', () => { + const step = { + productLineRequired: [ProductLine.cloud, ProductLine.endpoint], + id: ConfigureSteps.learnAbout, + } as Step; + const activeProducts = new Set([ProductLine.cloud]); - const isActive = isCardActive(card, activeProducts); + const isActive = isStepActive(step, activeProducts); expect(isActive).toBe(true); }); it('should return true if the card has no product type requirement', () => { - const card = {} as Card; + const step = { + id: ConfigureSteps.enablePrebuiltRules, + } as Step; const activeProducts = new Set([ProductLine.security]); - const isActive = isCardActive(card, activeProducts); + const isActive = isStepActive(step, activeProducts); expect(isActive).toBe(true); }); it('should return false if the card is not active based on the active products', () => { - const card = { productLineRequired: [ProductLine.security, ProductLine.cloud] } as Card; - const activeProducts = new Set([ProductLine.endpoint]); + const step = { + productLineRequired: [ProductLine.cloud, ProductLine.endpoint], + id: ConfigureSteps.learnAbout, + } as Step; + const activeProducts = new Set([ProductLine.security]); - const isActive = isCardActive(card, activeProducts); + const isActive = isStepActive(step, activeProducts); expect(isActive).toBe(false); }); }); -describe('setupCards', () => { - const analyticProductActiveCards = { - [SectionId.getSetUp]: { - [GetSetUpCardId.introduction]: { - id: GetSetUpCardId.introduction, - timeInMins: 3, - stepsLeft: 1, - }, - [GetSetUpCardId.bringInYourData]: { - id: GetSetUpCardId.bringInYourData, - timeInMins: 0, - stepsLeft: 0, - }, - [GetSetUpCardId.activateAndCreateRules]: { - id: GetSetUpCardId.activateAndCreateRules, - timeInMins: 0, - stepsLeft: 0, - }, - }, - [SectionId.getMoreFromElasticSecurity]: { - [GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow]: { - id: GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow, - stepsLeft: 0, - timeInMins: 0, - }, - [GetMoreFromElasticSecurityCardId.respondToThreats]: { - id: GetMoreFromElasticSecurityCardId.respondToThreats, - stepsLeft: 0, - timeInMins: 0, - }, - [GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace]: { - id: GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace, - stepsLeft: 0, - timeInMins: 0, - }, - }, +describe('setupActiveSections', () => { + const getCard = (cardId: CardId, sectionId: SectionId, activeSections: ActiveSections | null) => { + const section = activeSections ? activeSections[sectionId] : {}; + return section ? section[cardId] ?? { activeStepIds: null } : {}; }; - it('should set up active cards based on active products', () => { + + it('should set up active steps based on active products', () => { const finishedSteps = {} as unknown as Record>; const activeProducts = new Set([ProductLine.cloud]); - const activeCards = setupCards(finishedSteps, activeProducts); + const { activeSections } = setupActiveSections(finishedSteps, activeProducts); - expect(activeCards).toEqual({ - ...analyticProductActiveCards, - [SectionId.getSetUp]: { - ...analyticProductActiveCards[SectionId.getSetUp], - [GetSetUpCardId.protectYourEnvironmentInRealtime]: { - id: GetSetUpCardId.protectYourEnvironmentInRealtime, - timeInMins: 0, - stepsLeft: 0, - }, - }, - }); + expect( + getCard(GetSetUpCardId.introduction, SectionId.getSetUp, activeSections).activeStepIds + ).toEqual([IntroductionSteps.getToKnowElasticSecurity]); + + expect( + getCard(GetSetUpCardId.configure, SectionId.getSetUp, activeSections).activeStepIds + ).toEqual([ + ConfigureSteps.learnAbout, + ConfigureSteps.deployElasticAgent, + ConfigureSteps.enablePrebuiltRules, + ]); + + expect( + getCard(GetSetUpCardId.explore, SectionId.getSetUp, activeSections).activeStepIds + ).toEqual([ExploreSteps.viewAlerts, ExploreSteps.analyzeData]); }); - it('should skip inactive cards based on finished steps and active products', () => { - const finishedSteps = {} as Record>; + it('should set up active cards based on finished steps', () => { + const finishedSteps = { + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.getToKnowElasticSecurity]), + } as unknown as Record>; const activeProducts = new Set([ProductLine.security]); - const activeCards = setupCards(finishedSteps, activeProducts); + const { activeSections } = setupActiveSections(finishedSteps, activeProducts); - expect(activeCards).toEqual(analyticProductActiveCards); + expect(getCard(GetSetUpCardId.introduction, SectionId.getSetUp, activeSections)).toEqual({ + activeStepIds: [IntroductionSteps.getToKnowElasticSecurity], + id: GetSetUpCardId.introduction, + stepsLeft: 0, + timeInMins: 0, + }); }); it('should return null if there are no active products', () => { - const finishedSteps = { - [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), - } as unknown as Record>; + const finishedSteps = {} as unknown as Record>; const activeProducts: Set = new Set(); - const activeCards = setupCards(finishedSteps, activeProducts); + const activeSections = setupActiveSections(finishedSteps, activeProducts); - expect(activeCards).toBeNull(); + expect(activeSections).toEqual({ + activeSections: null, + totalActiveSteps: null, + totalStepsLeft: null, + }); }); it('should handle null or empty cards in sections', () => { @@ -182,118 +183,124 @@ describe('setupCards', () => { ]); const finishedSteps = { - [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.getToKnowElasticSecurity]), } as unknown as Record>; const activeProducts = new Set([ProductLine.security]); - const activeCards = setupCards(finishedSteps, activeProducts); + const activeSections = setupActiveSections(finishedSteps, activeProducts); - expect(activeCards).toEqual({}); + expect(activeSections).toEqual({ + activeSections: {}, + totalActiveSteps: 0, + totalStepsLeft: 0, + }); mockSections.mockRestore(); }); }); -describe('updateCard', () => { +describe('updateActiveSections', () => { const finishedSteps = { - [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.getToKnowElasticSecurity]), } as unknown as Record>; - const activeProducts = new Set([ProductLine.security, ProductLine.cloud]); - const activeCards = { + const activeSections = { [SectionId.getSetUp]: { [GetSetUpCardId.introduction]: { id: GetSetUpCardId.introduction, timeInMins: 3, stepsLeft: 1, }, - [GetSetUpCardId.bringInYourData]: { - id: GetSetUpCardId.bringInYourData, - timeInMins: 0, - stepsLeft: 0, - }, - [GetSetUpCardId.activateAndCreateRules]: { - id: GetSetUpCardId.activateAndCreateRules, - timeInMins: 0, - stepsLeft: 0, - }, - [GetSetUpCardId.protectYourEnvironmentInRealtime]: { - id: GetSetUpCardId.protectYourEnvironmentInRealtime, - timeInMins: 0, - stepsLeft: 0, - }, - }, - [SectionId.getMoreFromElasticSecurity]: { - [GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow]: { - id: GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow, - stepsLeft: 0, + [GetSetUpCardId.configure]: { + id: GetSetUpCardId.configure, timeInMins: 0, + stepsLeft: 4, }, - [GetMoreFromElasticSecurityCardId.respondToThreats]: { - id: GetMoreFromElasticSecurityCardId.respondToThreats, - stepsLeft: 0, - timeInMins: 0, - }, - [GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace]: { - id: GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace, - stepsLeft: 0, + [GetSetUpCardId.explore]: { + id: GetSetUpCardId.explore, timeInMins: 0, + stepsLeft: 2, }, }, - } as ActiveCards; + } as ActiveSections; it('should update the active card based on finished steps and active products', () => { + const activeProducts = new Set([ProductLine.cloud]); const sectionId = SectionId.getSetUp; const cardId = GetSetUpCardId.introduction; - - const updatedCards = updateCard({ - finishedSteps, + const testActiveSections = { + [SectionId.getSetUp]: { + [GetSetUpCardId.introduction]: { + id: GetSetUpCardId.introduction, + timeInMins: 3, + stepsLeft: 1, + activeStepIds: [IntroductionSteps.getToKnowElasticSecurity], + }, + }, + }; + const updatedSections = updateActiveSections({ activeProducts, - activeCards, - sectionId, + activeSections: testActiveSections, cardId, + finishedSteps, + sectionId, }); - expect(updatedCards).toEqual({ - ...activeCards, - [SectionId.getSetUp]: { - ...activeCards[SectionId.getSetUp], - [GetSetUpCardId.introduction]: { - id: GetSetUpCardId.introduction, - timeInMins: 0, - stepsLeft: 0, + expect(updatedSections).toEqual({ + activeSections: { + ...testActiveSections, + [SectionId.getSetUp]: { + ...testActiveSections[SectionId.getSetUp], + [GetSetUpCardId.introduction]: { + id: GetSetUpCardId.introduction, + timeInMins: 0, + stepsLeft: 0, + activeStepIds: [IntroductionSteps.getToKnowElasticSecurity], + }, }, }, + totalActiveSteps: 1, + totalStepsLeft: 0, }); }); it('should return null if the card is inactive based on active products', () => { + const activeProducts = new Set([ProductLine.cloud]); const sectionId = SectionId.getSetUp; - const cardId = GetSetUpCardId.protectYourEnvironmentInRealtime; + const cardId = GetSetUpCardId.introduction; - const updatedCards = updateCard({ - finishedSteps, + const updatedSections = updateActiveSections({ activeProducts, - activeCards: null, + finishedSteps, + activeSections: null, sectionId, cardId, }); - expect(updatedCards).toBeNull(); + expect(updatedSections).toEqual({ + activeSections: null, + totalStepsLeft: null, + totalActiveSteps: null, + }); }); - it('should return null if the card or activeCards is not found', () => { + it('should return null if the card or activeSections is not found', () => { + const activeProducts = new Set([ProductLine.cloud]); const sectionId = SectionId.getSetUp; const cardId = 'test' as CardId; - const updatedCards = updateCard({ - finishedSteps, + const updatedSections = updateActiveSections({ activeProducts, - activeCards, + finishedSteps, + activeSections, sectionId, cardId, }); - expect(updatedCards).toEqual(activeCards); + expect(updatedSections).toEqual({ + activeSections, + totalStepsLeft: null, + totalActiveSteps: null, + }); }); }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts index 202e00dc1a40a..746a8caa6b139 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts @@ -7,87 +7,187 @@ import type { ProductLine } from '../../common/product'; import { getSections } from './sections'; -import type { ActiveCard, ActiveCards, Card, CardId, SectionId, StepId } from './types'; +import type { ActiveCard, ActiveSections, CardId, SectionId, Step, StepId } from './types'; -export const getCardTimeInMinutes = (card: Card, stepsDone: Set) => - card.steps?.reduce( +export const getCardTimeInMinutes = (activeSteps: Step[] | undefined, stepsDone: Set) => + activeSteps?.reduce( (totalMin, { timeInMinutes, id: stepId }) => totalMin + (stepsDone.has(stepId) ? 0 : timeInMinutes ?? 0), 0 ) ?? 0; -export const getCardStepsLeft = (card: Card, stepsDone: Set) => - (card.steps?.length ?? 0) - (stepsDone.size ?? 0); +export const getCardStepsLeft = (activeSteps: Step[] | undefined, stepsDone: Set) => + (activeSteps?.length ?? 0) - (stepsDone.size ?? 0); -export const isCardActive = (card: Card, activeProducts: Set) => - !card.productLineRequired || - card.productLineRequired?.some((condition) => activeProducts.has(condition)); +export const isStepActive = (step: Step, activeProducts: Set) => + !step.productLineRequired || + step.productLineRequired?.some((condition) => activeProducts.has(condition)); -export const setupCards = ( +export const getActiveSteps = (steps: Step[] | undefined, activeProducts: Set) => + steps?.filter((step) => isStepActive(step, activeProducts)); + +const getfinishedActiveSteps = ( + finishedStepIds: StepId[] | undefined, + activeStepIds: StepId[] | undefined +) => { + const finishedActiveSteps = finishedStepIds?.reduce((acc, finishedStepId) => { + const activeFinishedStepId = activeStepIds?.find( + (activeStepId) => finishedStepId === activeStepId + ); + if (activeFinishedStepId) { + acc.push(activeFinishedStepId); + } + return acc; + }, [] as StepId[]); + + return new Set(finishedActiveSteps); +}; + +export const getCard = ({ cardId, sectionId }: { cardId: CardId; sectionId: SectionId }) => { + const sections = getSections(); + const section = sections.find(({ id }) => id === sectionId); + const cards = section?.cards; + const card = cards?.find(({ id }) => id === cardId); + + return card; +}; + +export const getStepsByActiveProduct = ({ + activeProducts, + cardId, + sectionId, +}: { + activeProducts: Set; + cardId: CardId; + sectionId: SectionId; +}) => { + const card = getCard({ cardId, sectionId }); + const steps = getActiveSteps(card?.steps, activeProducts); + + return steps; +}; + +export const setupActiveSections = ( finishedSteps: Record>, activeProducts: Set ) => activeProducts.size > 0 - ? getSections().reduce((acc, section) => { - const cardsInSections = section.cards?.reduce((accCards, card) => { - if (isCardActive(card, activeProducts)) { - const stepsDone: Set = finishedSteps[card.id] ?? new Set(); - const timeInMins = getCardTimeInMinutes(card, stepsDone); - const stepsLeft = getCardStepsLeft(card, stepsDone); - - accCards[card.id] = { - id: card.id, - timeInMins, - stepsLeft, - }; + ? getSections().reduce( + (acc, section) => { + const activeCards = + section.cards?.reduce((accCards, card) => { + const activeSteps = getActiveSteps(card.steps, activeProducts); + const activeStepIds = activeSteps?.map(({ id }) => id); + const stepsDone: Set = getfinishedActiveSteps( + finishedSteps[card.id] ? [...finishedSteps[card.id]] : undefined, + activeStepIds + ); + const timeInMins = getCardTimeInMinutes(activeSteps, stepsDone); + const stepsLeft = getCardStepsLeft(activeSteps, stepsDone); + acc.totalStepsLeft += stepsLeft; + acc.totalActiveSteps += activeStepIds?.length ?? 0; + + accCards[card.id] = { + id: card.id, + timeInMins, + stepsLeft, + activeStepIds, + }; + + return accCards; + }, {} as Record) ?? {}; + + if (Object.keys(activeCards).length > 0) { + acc.activeSections[section.id] = activeCards; } - return accCards; - }, {} as Record); - if (cardsInSections) { - acc[section.id] = cardsInSections; - } - return acc; - }, {} as ActiveCards) - : null; - -export const updateCard = ({ - finishedSteps, + return acc; + }, + { activeSections: {} as ActiveSections, totalStepsLeft: 0, totalActiveSteps: 0 } + ) + : { activeSections: null, totalStepsLeft: null, totalActiveSteps: null }; + +export const updateActiveSections = ({ activeProducts, - activeCards, - sectionId, + activeSections, cardId, + finishedSteps, + sectionId, }: { - finishedSteps: Record>; activeProducts: Set; - activeCards: ActiveCards | null; - sectionId: SectionId; + activeSections: ActiveSections | null; cardId: CardId; -}): ActiveCards | null => { - const sections = getSections(); - const section = sections.find(({ id }) => id === sectionId); - const cards = section?.cards; - const card = cards?.find(({ id }) => id === cardId); + finishedSteps: Record>; + sectionId: SectionId; +}): { + activeSections: ActiveSections | null; + totalStepsLeft: number | null; + totalActiveSteps: number | null; +} => { + const activeSection = activeSections ? activeSections[sectionId] : undefined; + const activeCard = activeSection ? activeSection[cardId] : undefined; - if (!card || !activeCards) { - return activeCards; + if (!activeCard || !activeSections) { + return { activeSections, totalActiveSteps: null, totalStepsLeft: null }; } - if (isCardActive(card, activeProducts)) { - const stepsDone = finishedSteps[cardId] ?? new Set(); - const timeInMins = getCardTimeInMinutes(card, stepsDone); - const stepsLeft = getCardStepsLeft(card, stepsDone); - - return { - ...activeCards, - [sectionId]: { - ...activeCards[sectionId], - [cardId]: { - id: cardId, - timeInMins, - stepsLeft, - }, + const steps = getStepsByActiveProduct({ activeProducts, cardId, sectionId }); + + const activeStepIds = activeCard.activeStepIds; + const stepsDone: Set = getfinishedActiveSteps( + finishedSteps[cardId] ? [...finishedSteps[cardId]] : undefined, + activeStepIds + ); + + const timeInMins = getCardTimeInMinutes(steps, stepsDone); + const stepsLeft = getCardStepsLeft(steps, stepsDone); + + const newActiveSections = { + ...activeSections, + [sectionId]: { + ...activeSections[sectionId], + [cardId]: { + id: cardId, + timeInMins, + stepsLeft, + activeStepIds, }, - }; - } - return activeCards; + }, + }; + + const { totalStepsLeft, totalActiveSteps } = Object.values(newActiveSections).reduce( + (acc, newActiveSection) => { + Object.values(newActiveSection).forEach( + (newActiveCard) => { + acc.totalStepsLeft += newActiveCard.stepsLeft; + acc.totalActiveSteps += newActiveCard?.activeStepIds?.length ?? 0; + }, + { totalStepsLeft: 0, totalActiveSteps: 0 } + ); + + return acc; + }, + { totalStepsLeft: 0, totalActiveSteps: 0 } + ); + + return { + activeSections: newActiveSections, + totalStepsLeft, + totalActiveSteps, + }; }; + +export const getTotalStepsLeftAndActiveSteps = (activeSections: ActiveSections | null) => + Object.values(activeSections ?? {}).reduce( + (acc, activeSection) => { + Object.values(activeSection).forEach( + (newActiveCard) => { + acc.totalStepsLeft += newActiveCard.stepsLeft; + acc.totalActiveSteps += newActiveCard?.activeStepIds?.length ?? 0; + }, + { totalStepsLeft: 0, totalActiveSteps: 0 } + ); + + return acc; + }, + { totalStepsLeft: 0, totalActiveSteps: 0 } + ); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/explore.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/explore.svg new file mode 100644 index 0000000000000..44db63f1c27ed --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/images/explore.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/icon_cross.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/icon_cross.svg new file mode 100644 index 0000000000000..4278382758373 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/images/icon_cross.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/step.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/icon_step.svg similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/step.svg rename to x-pack/plugins/security_solution_serverless/public/get_started/images/icon_step.svg diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/respond.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/respond.svg deleted file mode 100644 index 9e6a287b501d6..0000000000000 --- a/x-pack/plugins/security_solution_serverless/public/get_started/images/respond.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx index 5c54c87ccdd21..7f855c206274e 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx @@ -28,8 +28,8 @@ describe('ProductSwitch', () => { ); const analyticsSwitch = getByText('Analytics'); - const cloudSwitch = getByText('Cloud'); - const endpointSwitch = getByText('Endpoint'); + const cloudSwitch = getByText('Cloud Security'); + const endpointSwitch = getByText('Endpoint Security'); expect(analyticsSwitch).toBeInTheDocument(); expect(cloudSwitch).toBeInTheDocument(); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/progress_tracker.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/progress_tracker.tsx new file mode 100644 index 0000000000000..91a8b9a5965cb --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/progress_tracker.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import { WELCOME_PANEL_PROGRESS_TRACKER_DESCRIPTION } from './translations'; + +const ProgressTrackerComponent = ({ + totalActiveSteps, + totalStepsLeft, +}: { + totalActiveSteps?: number | null; + totalStepsLeft?: number | null; +}) => { + if (totalActiveSteps != null && totalStepsLeft != null) { + return ( + <> + + + + {WELCOME_PANEL_PROGRESS_TRACKER_DESCRIPTION(totalActiveSteps)} + + ); + } + + return null; +}; + +export const ProgressTracker = React.memo(ProgressTrackerComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts index fe38c68a1a160..71bd0bd8f9cf8 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts @@ -6,31 +6,42 @@ */ import { ProductLine } from '../../common/product'; +import { setupActiveSections } from './helpers'; import { reducer, getFinishedStepsInitialStates, + getActiveProductsInitialStates, getActiveSectionsInitialStates, - getActiveCardsInitialStates, } from './reducer'; +import type { ExpandedCardSteps } from './types'; import { GetSetUpCardId, GetStartedPageActions, IntroductionSteps, SectionId, - GetMoreFromElasticSecurityCardId, - type ActiveCards, type CardId, type StepId, type ToggleProductAction, type AddFinishedStepAction, + ConfigureSteps, + ExploreSteps, } from './types'; describe('reducer', () => { it('should toggle section correctly', () => { + const activeProducts = new Set([ProductLine.security]); + const finishedSteps = {} as Record>; + const { activeSections, totalStepsLeft, totalActiveSteps } = setupActiveSections( + finishedSteps, + activeProducts + ); const initialState = { activeProducts: new Set([ProductLine.security]), - finishedSteps: {} as Record>, - activeCards: {} as ActiveCards | null, + finishedSteps, + activeSections, + totalStepsLeft, + totalActiveSteps, + expandedCardSteps: {} as ExpandedCardSteps, }; const action: ToggleProductAction = { @@ -41,29 +52,30 @@ describe('reducer', () => { const nextState = reducer(initialState, action); expect(nextState.activeProducts.has(ProductLine.security)).toBe(false); - expect(nextState.activeCards).toBeNull(); + expect(nextState.activeSections).toBeNull(); }); it('should add a finished step correctly', () => { + const activeProducts = new Set([ProductLine.security]); + const finishedSteps = {} as Record>; + const { activeSections, totalStepsLeft, totalActiveSteps } = setupActiveSections( + finishedSteps, + activeProducts + ); const initialState = { activeProducts: new Set([ProductLine.security]), - finishedSteps: {} as Record>, - activeCards: { - getSetUp: { - [GetSetUpCardId.introduction]: { - id: GetSetUpCardId.introduction, - stepsLeft: 1, - timeInMins: 3, - }, - }, - } as unknown as ActiveCards | null, + finishedSteps, + activeSections, + totalStepsLeft, + totalActiveSteps, + expandedCardSteps: {} as ExpandedCardSteps, }; const action: AddFinishedStepAction = { type: GetStartedPageActions.AddFinishedStep, payload: { cardId: GetSetUpCardId.introduction, - stepId: IntroductionSteps.watchOverviewVideo, + stepId: IntroductionSteps.getToKnowElasticSecurity, sectionId: SectionId.getSetUp, }, }; @@ -71,14 +83,32 @@ describe('reducer', () => { const nextState = reducer(initialState, action); expect(nextState.finishedSteps[GetSetUpCardId.introduction]).toEqual( - new Set([IntroductionSteps.watchOverviewVideo]) + new Set([IntroductionSteps.getToKnowElasticSecurity]) ); - expect(nextState.activeCards).toEqual({ + expect(nextState.activeSections).toEqual({ getSetUp: { [GetSetUpCardId.introduction]: { id: GetSetUpCardId.introduction, stepsLeft: 0, timeInMins: 0, + activeStepIds: [IntroductionSteps.getToKnowElasticSecurity], + }, + [GetSetUpCardId.configure]: { + id: GetSetUpCardId.configure, + stepsLeft: 4, + timeInMins: 0, + activeStepIds: [ + ConfigureSteps.learnAbout, + ConfigureSteps.deployElasticAgent, + ConfigureSteps.connectToDataSources, + ConfigureSteps.enablePrebuiltRules, + ], + }, + [GetSetUpCardId.explore]: { + id: GetSetUpCardId.explore, + stepsLeft: 2, + timeInMins: 0, + activeStepIds: [ExploreSteps.viewAlerts, ExploreSteps.analyzeData], }, }, }); @@ -88,37 +118,44 @@ describe('reducer', () => { describe('getFinishedStepsInitialStates', () => { it('should return the initial states of finished steps correctly', () => { const finishedSteps = { - [GetSetUpCardId.introduction]: [IntroductionSteps.watchOverviewVideo], - [GetSetUpCardId.bringInYourData]: [], + [GetSetUpCardId.introduction]: [IntroductionSteps.getToKnowElasticSecurity], + [GetSetUpCardId.configure]: [], } as unknown as Record; const initialStates = getFinishedStepsInitialStates({ finishedSteps }); expect(initialStates[GetSetUpCardId.introduction]).toEqual( - new Set([IntroductionSteps.watchOverviewVideo]) + new Set([IntroductionSteps.getToKnowElasticSecurity]) ); - expect(initialStates[GetSetUpCardId.bringInYourData]).toEqual(new Set([])); + expect(initialStates[GetSetUpCardId.configure]).toEqual(new Set([])); }); }); -describe('getActiveSectionsInitialStates', () => { +describe('getActiveProductsInitialStates', () => { it('should return the initial states of active sections correctly', () => { const activeProducts = [ProductLine.security]; - const initialStates = getActiveSectionsInitialStates({ activeProducts }); + const initialStates = getActiveProductsInitialStates({ activeProducts }); expect(initialStates.has(ProductLine.security)).toBe(true); }); }); -describe('getActiveCardsInitialStates', () => { +describe('getActiveSectionsInitialStates', () => { it('should return the initial states of active cards correctly', () => { const activeProducts = new Set([ProductLine.security]); const finishedSteps = { - [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.getToKnowElasticSecurity]), } as unknown as Record>; - const initialStates = getActiveCardsInitialStates({ activeProducts, finishedSteps }); + const { + activeSections: initialStates, + totalActiveSteps, + totalStepsLeft, + } = getActiveSectionsInitialStates({ + activeProducts, + finishedSteps, + }); expect(initialStates).toEqual({ [SectionId.getSetUp]: { @@ -126,35 +163,29 @@ describe('getActiveCardsInitialStates', () => { id: GetSetUpCardId.introduction, timeInMins: 0, stepsLeft: 0, + activeStepIds: [IntroductionSteps.getToKnowElasticSecurity], }, - [GetSetUpCardId.bringInYourData]: { - id: GetSetUpCardId.bringInYourData, + [GetSetUpCardId.configure]: { + id: GetSetUpCardId.configure, timeInMins: 0, - stepsLeft: 0, + stepsLeft: 4, + activeStepIds: [ + ConfigureSteps.learnAbout, + ConfigureSteps.deployElasticAgent, + ConfigureSteps.connectToDataSources, + ConfigureSteps.enablePrebuiltRules, + ], }, - [GetSetUpCardId.activateAndCreateRules]: { - id: GetSetUpCardId.activateAndCreateRules, - timeInMins: 0, - stepsLeft: 0, - }, - }, - [SectionId.getMoreFromElasticSecurity]: { - [GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow]: { - id: GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow, - stepsLeft: 0, - timeInMins: 0, - }, - [GetMoreFromElasticSecurityCardId.respondToThreats]: { - id: GetMoreFromElasticSecurityCardId.respondToThreats, - stepsLeft: 0, - timeInMins: 0, - }, - [GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace]: { - id: GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace, - stepsLeft: 0, + [GetSetUpCardId.explore]: { + id: GetSetUpCardId.explore, timeInMins: 0, + stepsLeft: 2, + activeStepIds: [ExploreSteps.viewAlerts, ExploreSteps.analyzeData], }, }, }); + + expect(totalActiveSteps).toEqual(7); + expect(totalStepsLeft).toEqual(6); }); }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx index f59911aa11808..a18f798e582c0 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx @@ -6,20 +6,11 @@ */ import type { ProductLine } from '../../common/product'; -import { setupCards, updateCard } from './helpers'; -import { - type CardId, - type StepId, - type ToggleProductAction, - type TogglePanelReducer, - type AddFinishedStepAction, - GetStartedPageActions, -} from './types'; - -export const reducer = ( - state: TogglePanelReducer, - action: ToggleProductAction | AddFinishedStepAction -): TogglePanelReducer => { +import { setupActiveSections, updateActiveSections } from './helpers'; +import type { ReducerActions } from './types'; +import { type CardId, type StepId, type TogglePanelReducer, GetStartedPageActions } from './types'; + +export const reducer = (state: TogglePanelReducer, action: ReducerActions): TogglePanelReducer => { if (action.type === GetStartedPageActions.ToggleProduct) { const activeProducts = new Set([...state.activeProducts]); @@ -29,10 +20,17 @@ export const reducer = ( activeProducts.add(action.payload.section); } + const { activeSections, totalStepsLeft, totalActiveSteps } = setupActiveSections( + state.finishedSteps, + activeProducts + ); + return { ...state, activeProducts, - activeCards: setupCards(state.finishedSteps, activeProducts), + activeSections, + totalStepsLeft, + totalActiveSteps, }; } @@ -44,18 +42,95 @@ export const reducer = ( : new Set(), }; - finishedSteps[action.payload.cardId].add(action.payload.stepId); + if (action.type === GetStartedPageActions.AddFinishedStep) { + finishedSteps[action.payload.cardId].add(action.payload.stepId); + } + + const { activeSections, totalStepsLeft, totalActiveSteps } = updateActiveSections({ + activeProducts: state.activeProducts, + activeSections: state.activeSections, + cardId: action.payload.cardId, + finishedSteps, + sectionId: action.payload.sectionId, + }); return { ...state, finishedSteps, - activeCards: updateCard({ - finishedSteps, - activeProducts: state.activeProducts, - activeCards: state.activeCards, - cardId: action.payload.cardId, - sectionId: action.payload.sectionId, - }), + activeSections, + totalStepsLeft, + totalActiveSteps, + }; + } + + if (action.type === GetStartedPageActions.RemoveFinishedStep) { + const finishedSteps = { + ...state.finishedSteps, + [action.payload.cardId]: state.finishedSteps[action.payload.cardId] + ? new Set([...state.finishedSteps[action.payload.cardId]]) + : new Set(), + }; + + if (action.type === GetStartedPageActions.RemoveFinishedStep) { + finishedSteps[action.payload.cardId].delete(action.payload.stepId); + } + + const { activeSections, totalStepsLeft, totalActiveSteps } = updateActiveSections({ + activeProducts: state.activeProducts, + activeSections: state.activeSections, + cardId: action.payload.cardId, + finishedSteps, + sectionId: action.payload.sectionId, + }); + + return { + ...state, + finishedSteps, + activeSections, + totalStepsLeft, + totalActiveSteps, + }; + } + + if ( + action.type === GetStartedPageActions.ToggleExpandedCardStep && + action.payload.isCardExpanded != null + ) { + return { + ...state, + expandedCardSteps: { + ...state.expandedCardSteps, + [action.payload.cardId]: { + expandedSteps: state.expandedCardSteps[action.payload.cardId]?.expandedSteps ?? [], + isExpanded: action.payload.isCardExpanded, + }, + }, + }; + } + + if ( + action.type === GetStartedPageActions.ToggleExpandedCardStep && + action.payload.isStepExpanded != null + ) { + const expandedSteps = new Set( + [...state.expandedCardSteps[action.payload.cardId]?.expandedSteps] ?? [] + ); + if (action.payload.isStepExpanded === true && action.payload.stepId) { + expandedSteps.add(action.payload.stepId); + } + + if (action.payload.isStepExpanded === false && action.payload.stepId) { + expandedSteps.delete(action.payload.stepId); + } + return { + ...state, + expandedCardSteps: { + ...state.expandedCardSteps, + [action.payload.cardId]: { + expandedSteps: [...expandedSteps], + isExpanded: state.expandedCardSteps[action.payload.cardId]?.isExpanded, + }, + }, }; } @@ -77,16 +152,16 @@ export const getFinishedStepsInitialStates = ({ return initialStates; }; -export const getActiveSectionsInitialStates = ({ +export const getActiveProductsInitialStates = ({ activeProducts, }: { activeProducts: ProductLine[]; }) => new Set(activeProducts); -export const getActiveCardsInitialStates = ({ +export const getActiveSectionsInitialStates = ({ activeProducts, finishedSteps, }: { activeProducts: Set; finishedSteps: Record>; -}) => setupCards(finishedSteps, activeProducts); +}) => setupActiveSections(finishedSteps, activeProducts); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx index 11b15264d53ea..c056e463dfb88 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx @@ -5,27 +5,31 @@ * 2.0. */ import React from 'react'; + import { SectionId, - GetMoreFromElasticSecurityCardId, GetSetUpCardId, IntroductionSteps, type Section, + ConfigureSteps, + ExploreSteps, } from './types'; import * as i18n from './translations'; -import respond from './images/respond.svg'; -import protect from './images/protect.svg'; +import explore from './images/explore.svg'; import { ProductLine } from '../../common/product'; +import { FleetOverviewLink } from './step_links/fleet_overview_link'; +import { EndpointManagementLink } from './step_links/endpoint_management_link'; +import { IntegrationsLink } from './step_links/integrations_link'; +import { RulesManagementLink } from './step_links/rules_management_link'; +import { OverviewLink } from './step_links/overview_link'; +import { AlertsLink } from './step_links/alerts_link'; +import { ExploreLink } from './step_links/explore_link'; export const introductionSteps = [ { - id: IntroductionSteps.watchOverviewVideo, - title: i18n.WATCH_OVERVIEW_VIDEO_TITLE, - description: [ - i18n.WATCH_OVERVIEW_VIDEO_DESCRIPTION1, - i18n.WATCH_OVERVIEW_VIDEO_DESCRIPTION2, - i18n.WATCH_OVERVIEW_VIDEO_DESCRIPTION3, - ], + id: IntroductionSteps.getToKnowElasticSecurity, + title: i18n.INTRODUCTION_STEP1, + description: [i18n.INTRODUCTION_STEP1_DESCRIPTION1, i18n.INTRODUCTION_STEP1_DESCRIPTION2], splitPanel: (