From 935ba166f91898c0800d845d2d23e7b6bb074394 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 12 Oct 2021 23:48:00 -0700 Subject: [PATCH 001/117] [Fleet] Show security requirements page when ES security is not enabled (#114583) * Show security requirements page when ES security is not enabled * Add comments --- .../common/types/rest_spec/fleet_setup.ts | 1 + .../fleet/public/applications/fleet/app.tsx | 101 +++++++++--------- .../es_requirements_page.tsx | 2 +- .../plugins/fleet/server/routes/app/index.ts | 18 +++- .../translations/translations/ja-JP.json | 21 ++-- .../translations/translations/zh-CN.json | 21 ++-- 6 files changed, 86 insertions(+), 78 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts b/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts index a637f19423a6b..198aa111c6fb0 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts @@ -13,6 +13,7 @@ export interface PostFleetSetupResponse { export interface GetFleetStatusResponse { isReady: boolean; missing_requirements: Array< + | 'security_required' | 'tls_required' | 'api_keys' | 'fleet_admin_user' diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index 11be87f146851..682c889d80b97 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -45,6 +45,7 @@ import { DefaultLayout, DefaultPageTitle, WithoutHeaderLayout, WithHeaderLayout import { AgentPolicyApp } from './sections/agent_policy'; import { DataStreamApp } from './sections/data_stream'; import { AgentsApp } from './sections/agents'; +import { MissingESRequirementsPage } from './sections/agents/agent_requirements_page'; import { CreatePackagePolicyPage } from './sections/agent_policy/create_package_policy_page'; import { EnrollmentTokenListPage } from './sections/agents/enrollment_token_list_page'; @@ -71,6 +72,53 @@ const Panel = styled(EuiPanel)` margin-left: auto; `; +const PermissionsError: React.FunctionComponent<{ error: string }> = memo(({ error }) => { + if (error === 'MISSING_SECURITY') { + return ; + } + + if (error === 'MISSING_SUPERUSER_ROLE') { + return ( + + + + + } + body={ +

+ superuser }} + /> +

+ } + /> +
+ ); + } + + return ( + + } + error={i18n.translate('xpack.fleet.permissionsRequestErrorMessageDescription', { + defaultMessage: 'There was a problem checking Fleet permissions', + })} + /> + ); +}); + export const WithPermissionsAndSetup: React.FC = memo(({ children }) => { useBreadcrumbs('base'); const { notifications } = useStartServices(); @@ -121,58 +169,7 @@ export const WithPermissionsAndSetup: React.FC = memo(({ children }) => { if (isPermissionsLoading || permissionsError) { return ( - {isPermissionsLoading ? ( - - ) : permissionsError === 'REQUEST_ERROR' ? ( - - } - error={i18n.translate('xpack.fleet.permissionsRequestErrorMessageDescription', { - defaultMessage: 'There was a problem checking Fleet permissions', - })} - /> - ) : ( - - - {permissionsError === 'MISSING_SUPERUSER_ROLE' ? ( - - ) : ( - - )} - - } - body={ -

- {permissionsError === 'MISSING_SUPERUSER_ROLE' ? ( - superuser }} - /> - ) : ( - - )} -

- } - /> -
- )} + {isPermissionsLoading ? : }
); } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx index 9c5ec12645c1d..5aff449a923b3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx @@ -76,7 +76,7 @@ export const MissingESRequirementsPage: React.FunctionComponent<{ values={{ esConfigFile: elasticsearch.yml }} /> - + { + const missingSecurityBody: CheckPermissionsResponse = { + success: false, + error: 'MISSING_SECURITY', + }; const body: CheckPermissionsResponse = { success: true }; try { const security = await appContextService.getSecurity(); const user = security.authc.getCurrentUser(request); + // when ES security is disabled, but Kibana security plugin is not explicitly disabled, + // `authc.getCurrentUser()` does not error, instead it comes back as `null` + if (!user) { + return response.ok({ + body: missingSecurityBody, + }); + } + if (!user?.roles.includes('superuser')) { body.success = false; body.error = 'MISSING_SUPERUSER_ROLE'; @@ -28,10 +40,10 @@ export const getCheckPermissionsHandler: RequestHandler = async (context, reques return response.ok({ body: { success: true } }); } catch (e) { - body.success = false; - body.error = 'MISSING_SECURITY'; + // when Kibana security plugin is explicitly disabled, + // `appContextService.getSecurity()` returns an error, so we catch it here return response.ok({ - body, + body: missingSecurityBody, }); } }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 06f5e133f33b6..84d87cb020fe9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4908,6 +4908,16 @@ "visTypeMetric.colorModes.backgroundOptionLabel": "背景", "visTypeMetric.colorModes.labelsOptionLabel": "ラベル", "visTypeMetric.colorModes.noneOptionLabel": "なし", + "visTypeMetric.metricDescription": "計算結果を単独の数字として表示します。", + "visTypeMetric.metricTitle": "メトリック", + "visTypeMetric.params.color.useForLabel": "使用する色", + "visTypeMetric.params.rangesTitle": "範囲", + "visTypeMetric.params.settingsTitle": "設定", + "visTypeMetric.params.showTitleLabel": "タイトルを表示", + "visTypeMetric.params.style.fontSizeLabel": "ポイント単位のメトリックフォントサイズ", + "visTypeMetric.params.style.styleTitle": "スタイル", + "visTypeMetric.schemas.metricTitle": "メトリック", + "visTypeMetric.schemas.splitGroupTitle": "グループを分割", "expressionMetricVis.function.dimension.splitGroup": "グループを分割", "expressionMetricVis.function.bgFill.help": "html 16 進数コード(#123456)、html 色(red、blue)、または rgba 値(rgba(255,255,255,1))。", "expressionMetricVis.function.bucket.help": "バケットディメンションの構成です。", @@ -4923,16 +4933,6 @@ "expressionMetricVis.function.showLabels.help": "メトリック値の下にラベルを表示します。", "expressionMetricVis.function.subText.help": "メトリックの下に表示するカスタムテキスト", "expressionMetricVis.function.useRanges.help": "有効な色範囲です。", - "visTypeMetric.metricDescription": "計算結果を単独の数字として表示します。", - "visTypeMetric.metricTitle": "メトリック", - "visTypeMetric.params.color.useForLabel": "使用する色", - "visTypeMetric.params.rangesTitle": "範囲", - "visTypeMetric.params.settingsTitle": "設定", - "visTypeMetric.params.showTitleLabel": "タイトルを表示", - "visTypeMetric.params.style.fontSizeLabel": "ポイント単位のメトリックフォントサイズ", - "visTypeMetric.params.style.styleTitle": "スタイル", - "visTypeMetric.schemas.metricTitle": "メトリック", - "visTypeMetric.schemas.splitGroupTitle": "グループを分割", "visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.deprecation": "Visualizeの円グラフのレガシーグラフライブラリは廃止予定であり、8.0以降ではサポートされません。", "visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.description": "Visualizeで円グラフのレガシーグラフライブラリを有効にします。", "visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.name": "円グラフのレガシーグラフライブラリ", @@ -10947,7 +10947,6 @@ "xpack.fleet.preconfiguration.missingIDError": "{agentPolicyName}には「id」フィールドがありません。ポリシーのis_defaultまたはis_default_fleet_serverに設定されている場合をのぞき、「id」は必須です。", "xpack.fleet.preconfiguration.packageMissingError": "{agentPolicyName}を追加できませんでした。{pkgName}がインストールされていません。{pkgName}を`{packagesConfigValue}`に追加するか、{packagePolicyName}から削除してください。", "xpack.fleet.preconfiguration.policyDeleted": "構成済みのポリシー{id}が削除されました。作成をスキップしています", - "xpack.fleet.securityRequiredErrorMessage": "Fleet を使用するには、Kibana と Elasticsearch でセキュリティを有効にする必要があります。", "xpack.fleet.securityRequiredErrorTitle": "セキュリティが有効ではありません", "xpack.fleet.serverError.agentPolicyDoesNotExist": "エージェントポリシー{agentPolicyId}が存在しません", "xpack.fleet.serverError.enrollmentKeyDuplicate": "エージェントポリシーの{agentPolicyId}登録キー{providedKeyName}はすでに存在します", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d8c35485f2dbc..fa78b2eae1432 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4953,6 +4953,16 @@ "visTypeMetric.colorModes.backgroundOptionLabel": "背景", "visTypeMetric.colorModes.labelsOptionLabel": "标签", "visTypeMetric.colorModes.noneOptionLabel": "无", + "visTypeMetric.metricDescription": "将计算结果显示为单个数字。", + "visTypeMetric.metricTitle": "指标", + "visTypeMetric.params.color.useForLabel": "将颜色用于", + "visTypeMetric.params.rangesTitle": "范围", + "visTypeMetric.params.settingsTitle": "设置", + "visTypeMetric.params.showTitleLabel": "显示标题", + "visTypeMetric.params.style.fontSizeLabel": "指标字体大小(磅)", + "visTypeMetric.params.style.styleTitle": "样式", + "visTypeMetric.schemas.metricTitle": "指标", + "visTypeMetric.schemas.splitGroupTitle": "拆分组", "expressionMetricVis.function.dimension.splitGroup": "拆分组", "expressionMetricVis.function.bgFill.help": "将颜色表示为 html 十六进制代码 (#123456)、html 颜色(red、blue)或 rgba 值 (rgba(255,255,255,1))。", "expressionMetricVis.function.bucket.help": "存储桶维度配置", @@ -4968,16 +4978,6 @@ "expressionMetricVis.function.showLabels.help": "在指标值下显示标签。", "expressionMetricVis.function.subText.help": "要在指标下显示的定制文本", "expressionMetricVis.function.useRanges.help": "已启用颜色范围。", - "visTypeMetric.metricDescription": "将计算结果显示为单个数字。", - "visTypeMetric.metricTitle": "指标", - "visTypeMetric.params.color.useForLabel": "将颜色用于", - "visTypeMetric.params.rangesTitle": "范围", - "visTypeMetric.params.settingsTitle": "设置", - "visTypeMetric.params.showTitleLabel": "显示标题", - "visTypeMetric.params.style.fontSizeLabel": "指标字体大小(磅)", - "visTypeMetric.params.style.styleTitle": "样式", - "visTypeMetric.schemas.metricTitle": "指标", - "visTypeMetric.schemas.splitGroupTitle": "拆分组", "visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.deprecation": "Visualize 中饼图的旧版图表库已弃用,自 8.0 后将不受支持。", "visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.description": "在 Visualize 中启用饼图的旧版图表库。", "visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.name": "饼图旧版图表库", @@ -11061,7 +11061,6 @@ "xpack.fleet.preconfiguration.missingIDError": "{agentPolicyName} 缺失 `id` 字段。`id` 是必需的,但标记为 is_default 或 is_default_fleet_server 的策略除外。", "xpack.fleet.preconfiguration.packageMissingError": "{agentPolicyName} 无法添加。{pkgName} 未安装,请将 {pkgName} 添加到 `{packagesConfigValue}` 或将其从 {packagePolicyName} 中移除。", "xpack.fleet.preconfiguration.policyDeleted": "预配置的策略 {id} 已删除;将跳过创建", - "xpack.fleet.securityRequiredErrorMessage": "必须在 Kibana 和 Elasticsearch 启用安全性,才能使用 Fleet。", "xpack.fleet.securityRequiredErrorTitle": "安全性未启用", "xpack.fleet.serverError.agentPolicyDoesNotExist": "代理策略 {agentPolicyId} 不存在", "xpack.fleet.serverError.enrollmentKeyDuplicate": "称作 {providedKeyName} 的注册密钥对于代理策略 {agentPolicyId} 已存在", From 3d75154368a6dbf7694607378c90f78c67e664ce Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Wed, 13 Oct 2021 03:16:21 -0400 Subject: [PATCH 002/117] [APM] Removes the apm_oss plugin and deprecates related configs (#113364) * [APM] Removes the apm_oss plugin and deprecates related configs (#108160) * removes commented lines * fixes typescript errors * performs start up migration on the saved objects mapping for apm-indices * removes all references to the deprecated apm_oss.* config paths in APM * fixes missing bundle error * fixes type error * fixes eslint error with disabled rules * fixes saved object mappings for es_archvices for tests * fixes eslint error * Updates default index values to include the more general apm-*. Fixes broken tests. * removing unused configs from the config path migration: apm_oss.indexPattern and apm_oss.fleetMode * - replaces full index configuration paths with references in the `xpack.apm.indices` namespace - removes mergeConfig function and test * fixes proxy mock object in unit test * fixes linting issues * PR feedback and failed test * changes the configs at `xpack.apm.indices.*` from plural to singular to match `processor.event` values --- docs/developer/plugin-list.asciidoc | 4 - packages/kbn-optimizer/limits.yml | 1 - .../resources/base/bin/kibana-docker | 6 ++ src/plugins/apm_oss/README.asciidoc | 5 - src/plugins/apm_oss/kibana.json | 13 --- src/plugins/apm_oss/public/index.ts | 16 --- src/plugins/apm_oss/public/plugin.ts | 22 ----- src/plugins/apm_oss/public/types.ts | 16 --- src/plugins/apm_oss/server/index.ts | 40 -------- src/plugins/apm_oss/server/plugin.ts | 30 ------ src/plugins/apm_oss/tsconfig.json | 17 ---- .../integration/power_user/no_data_screen.ts | 25 +++-- x-pack/plugins/apm/kibana.json | 1 - .../app/Settings/ApmIndices/index.tsx | 19 ++-- .../plugins/apm/public/utils/testHelpers.tsx | 28 +++--- .../apm/scripts/shared/read-kibana-config.ts | 14 ++- .../scripts/upload-telemetry-data/index.ts | 7 +- x-pack/plugins/apm/server/index.test.ts | 40 -------- x-pack/plugins/apm/server/index.ts | 97 ++++++------------- .../alerts/register_error_count_alert_type.ts | 2 +- ...egister_transaction_duration_alert_type.ts | 6 +- ...ister_transaction_error_rate_alert_type.ts | 6 +- .../apm/server/lib/alerts/test_utils/index.ts | 8 +- .../create_anomaly_detection_jobs.ts | 2 +- .../collect_data_telemetry/tasks.test.ts | 10 +- .../collect_data_telemetry/tasks.ts | 78 +++++++-------- .../errors/distribution/get_buckets.test.ts | 14 ++- .../get_is_using_transaction_events.test.ts | 9 +- .../get_is_using_transaction_events.ts | 3 +- .../helpers/aggregated_transactions/index.ts | 3 +- .../unpack_processor_events.test.ts | 13 ++- .../unpack_processor_events.ts | 19 ++-- .../server/lib/helpers/setup_request.test.ts | 23 ++--- .../create_static_index_pattern.test.ts | 25 ++--- .../create_static_index_pattern.ts | 2 +- .../get_apm_index_pattern_title.test.ts | 18 ++-- .../get_apm_index_pattern_title.ts | 8 +- .../java/gc/fetch_and_transform_gc_metrics.ts | 2 +- .../metrics/fetch_and_transform_metrics.ts | 2 +- .../apm/server/lib/rum_client/has_rum_data.ts | 4 +- ...ransactions_correlations_search_service.ts | 2 +- .../latency_correlations_search_service.ts | 2 +- .../search_strategy_provider.test.ts | 5 +- .../server/lib/service_map/get_service_map.ts | 5 +- .../get_service_map_service_node_info.test.ts | 4 +- .../lib/service_map/get_trace_sample_ids.ts | 13 +-- .../get_service_transaction_groups.ts | 2 +- .../settings/apm_indices/get_apm_indices.ts | 37 +++---- .../traces/__snapshots__/queries.test.ts.snap | 2 +- .../apm/server/lib/traces/get_trace_items.ts | 8 +- .../lib/transactions/breakdown/index.test.ts | 19 ++-- .../lib/transactions/breakdown/index.ts | 2 +- x-pack/plugins/apm/server/plugin.ts | 26 ++--- x-pack/plugins/apm/server/routes/fleet.ts | 6 +- .../plugins/apm/server/routes/service_map.ts | 6 +- .../apm/server/routes/settings/apm_indices.ts | 21 ++-- .../apm/server/saved_objects/apm_indices.ts | 42 ++++---- .../migrations/update_apm_oss_index_paths.ts | 36 +++++++ .../apm/server/tutorial/envs/on_prem.ts | 16 +-- x-pack/plugins/apm/server/tutorial/index.ts | 2 +- x-pack/plugins/apm/server/types.ts | 6 -- .../plugins/apm/server/utils/test_helpers.tsx | 44 ++++----- x-pack/plugins/apm/tsconfig.json | 1 - .../plugins/observability/common/typings.ts | 14 ++- .../hooks/use_app_index_pattern.tsx | 2 +- .../public/context/has_data_context.test.tsx | 5 +- .../observability/public/data_handler.test.ts | 5 +- .../pages/overview/overview.stories.tsx | 5 +- .../encrypted_saved_objects/mappings.json | 38 ++++---- .../mappings.json | 38 ++++---- .../es_archiver/key_rotation/mappings.json | 38 ++++---- .../action_task_params/mappings.json | 38 ++++---- .../es_archives/actions/mappings.json | 38 ++++---- .../es_archives/alerts_legacy/mappings.json | 38 ++++---- .../es_archives/canvas/reports/mappings.json | 38 ++++---- .../cases/migrations/7.10.0/mappings.json | 38 ++++---- .../cases/migrations/7.11.1/mappings.json | 40 ++++---- .../cases/migrations/7.13.2/mappings.json | 40 ++++---- .../7.13_user_actions/mappings.json | 40 ++++---- .../migrations/7.16.0_space/mappings.json | 38 ++++---- .../data/search_sessions/mappings.json | 38 ++++---- .../telemetry/agent_only/mappings.json | 38 ++++---- .../mappings.json | 38 ++++---- .../cloned_endpoint_installed/mappings.json | 38 ++++---- .../cloned_endpoint_uninstalled/mappings.json | 38 ++++---- .../endpoint_malware_disabled/mappings.json | 38 ++++---- .../endpoint_malware_enabled/mappings.json | 38 ++++---- .../endpoint_uninstalled/mappings.json | 38 ++++---- .../es_archives/fleet/agents/mappings.json | 38 ++++---- .../mappings.json | 38 ++++---- .../es_archives/lists/mappings.json | 38 ++++---- .../canvas_disallowed_url/mappings.json | 38 ++++---- .../ecommerce_kibana_spaces/mappings.json | 38 ++++---- .../reporting/hugedata/mappings.json | 38 ++++---- .../multi_index_kibana/mappings.json | 40 ++++---- .../migrations/mappings.json | 38 ++++---- .../timelines/7.15.0/mappings.json | 38 ++++---- .../timelines/7.15.0_space/mappings.json | 40 ++++---- .../visualize/default/mappings.json | 38 ++++---- 99 files changed, 885 insertions(+), 1299 deletions(-) delete mode 100644 src/plugins/apm_oss/README.asciidoc delete mode 100644 src/plugins/apm_oss/kibana.json delete mode 100644 src/plugins/apm_oss/public/index.ts delete mode 100644 src/plugins/apm_oss/public/plugin.ts delete mode 100644 src/plugins/apm_oss/public/types.ts delete mode 100644 src/plugins/apm_oss/server/index.ts delete mode 100644 src/plugins/apm_oss/server/plugin.ts delete mode 100644 src/plugins/apm_oss/tsconfig.json delete mode 100644 x-pack/plugins/apm/server/index.test.ts create mode 100644 x-pack/plugins/apm/server/saved_objects/migrations/update_apm_oss_index_paths.ts diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 17cc974ed4466..9581848be9e53 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -28,10 +28,6 @@ allowing users to configure their advanced settings, also known as uiSettings within the code. -|{kib-repo}blob/{branch}/src/plugins/apm_oss/README.asciidoc[apmOss] -|undefined - - |{kib-repo}blob/{branch}/src/plugins/bfetch/README.md[bfetch] |bfetch allows to batch HTTP requests and streams responses back. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index acef661d93bb0..068408bce40fd 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -2,7 +2,6 @@ pageLoadAssetSize: advancedSettings: 27596 alerting: 106936 apm: 64385 - apmOss: 18996 bfetch: 51874 canvas: 1066647 charts: 95000 diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 2f3c610d905e7..9e7766ce16c9b 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -211,6 +211,12 @@ kibana_vars=( xpack.alerts.invalidateApiKeysTask.interval xpack.alerts.invalidateApiKeysTask.removalDelay xpack.apm.enabled + xpack.apm.indices.errors + xpack.apm.indices.metrics + xpack.apm.indices.onboarding + xpack.apm.indices.sourcemaps + xpack.apm.indices.spans + xpack.apm.indices.transactions xpack.apm.maxServiceEnvironments xpack.apm.searchAggregatedTransactions xpack.apm.serviceMapEnabled diff --git a/src/plugins/apm_oss/README.asciidoc b/src/plugins/apm_oss/README.asciidoc deleted file mode 100644 index c3c060a99ee27..0000000000000 --- a/src/plugins/apm_oss/README.asciidoc +++ /dev/null @@ -1,5 +0,0 @@ -# APM OSS plugin - -OSS plugin for APM. Includes index configuration and tutorial resources. - -See <<../../x-pack/plugins/apm/readme.md,the X-Pack APM plugin README>> for information about the main APM plugin. diff --git a/src/plugins/apm_oss/kibana.json b/src/plugins/apm_oss/kibana.json deleted file mode 100644 index f18b275add9e3..0000000000000 --- a/src/plugins/apm_oss/kibana.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "id": "apmOss", - "owner": { - "name": "APM UI", - "githubTeam": "apm-ui" - }, - "version": "8.0.0", - "server": true, - "kibanaVersion": "kibana", - "configPath": ["apm_oss"], - "ui": true, - "requiredPlugins": ["home"] -} diff --git a/src/plugins/apm_oss/public/index.ts b/src/plugins/apm_oss/public/index.ts deleted file mode 100644 index fea8ac4a8a1e4..0000000000000 --- a/src/plugins/apm_oss/public/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { ApmOssPlugin } from './plugin'; - -// This exports static code and TypeScript types, -// as well as, Kibana Platform `plugin()` initializer. -export function plugin() { - return new ApmOssPlugin(); -} -export { ApmOssPluginSetup, ApmOssPluginStart } from './types'; diff --git a/src/plugins/apm_oss/public/plugin.ts b/src/plugins/apm_oss/public/plugin.ts deleted file mode 100644 index e6981954b301a..0000000000000 --- a/src/plugins/apm_oss/public/plugin.ts +++ /dev/null @@ -1,22 +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 { CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { ApmOssPluginSetup, ApmOssPluginStart } from './types'; - -export class ApmOssPlugin implements Plugin { - public setup(core: CoreSetup): ApmOssPluginSetup { - return {}; - } - - public start(core: CoreStart): ApmOssPluginStart { - return {}; - } - - public stop() {} -} diff --git a/src/plugins/apm_oss/public/types.ts b/src/plugins/apm_oss/public/types.ts deleted file mode 100644 index 16a1fe8be8004..0000000000000 --- a/src/plugins/apm_oss/public/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ApmOssPluginSetup {} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ApmOssPluginStart {} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AppPluginStartDependencies {} diff --git a/src/plugins/apm_oss/server/index.ts b/src/plugins/apm_oss/server/index.ts deleted file mode 100644 index f2f6777672e33..0000000000000 --- a/src/plugins/apm_oss/server/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { schema, TypeOf } from '@kbn/config-schema'; -import { ConfigDeprecationProvider, PluginInitializerContext } from '../../../core/server'; -import { APMOSSPlugin } from './plugin'; - -const deprecations: ConfigDeprecationProvider = ({ deprecate, unused }) => [ - deprecate('enabled', '8.0.0'), - unused('fleetMode'), - unused('indexPattern'), -]; - -export const config = { - schema: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - transactionIndices: schema.string({ defaultValue: 'apm-*' }), - spanIndices: schema.string({ defaultValue: 'apm-*' }), - errorIndices: schema.string({ defaultValue: 'apm-*' }), - metricsIndices: schema.string({ defaultValue: 'apm-*' }), - sourcemapIndices: schema.string({ defaultValue: 'apm-*' }), - onboardingIndices: schema.string({ defaultValue: 'apm-*' }), - indexPattern: schema.string({ defaultValue: 'apm-*' }), - fleetMode: schema.boolean({ defaultValue: true }), - }), - deprecations, -}; - -export function plugin(initializerContext: PluginInitializerContext) { - return new APMOSSPlugin(initializerContext); -} - -export type APMOSSConfig = TypeOf; - -export { APMOSSPluginSetup } from './plugin'; diff --git a/src/plugins/apm_oss/server/plugin.ts b/src/plugins/apm_oss/server/plugin.ts deleted file mode 100644 index 02a8ac38be2a3..0000000000000 --- a/src/plugins/apm_oss/server/plugin.ts +++ /dev/null @@ -1,30 +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 { Observable } from 'rxjs'; -import { Plugin, PluginInitializerContext } from 'src/core/server'; -import { APMOSSConfig } from './'; - -export class APMOSSPlugin implements Plugin { - constructor(private readonly initContext: PluginInitializerContext) { - this.initContext = initContext; - } - public setup() { - const config$ = this.initContext.config.create(); - const config = this.initContext.config.get(); - return { config, config$ }; - } - - start() {} - stop() {} -} - -export interface APMOSSPluginSetup { - config: APMOSSConfig; - config$: Observable; -} diff --git a/src/plugins/apm_oss/tsconfig.json b/src/plugins/apm_oss/tsconfig.json deleted file mode 100644 index 08ed86d5da0a8..0000000000000 --- a/src/plugins/apm_oss/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 - "server/tutorial/index_pattern.json" - ], - "references": [{ "path": "../../core/tsconfig.json" }, { "path": "../home/tsconfig.json" }] -} diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts index 1e954d9982295..47eba11e6f6fb 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ const apmIndicesSaveURL = '/internal/apm/settings/apm-indices/save'; @@ -20,12 +19,12 @@ describe('No data screen', () => { url: apmIndicesSaveURL, method: 'POST', body: { - 'apm_oss.sourcemapIndices': 'foo-*', - 'apm_oss.errorIndices': 'foo-*', - 'apm_oss.onboardingIndices': 'foo-*', - 'apm_oss.spanIndices': 'foo-*', - 'apm_oss.transactionIndices': 'foo-*', - 'apm_oss.metricsIndices': 'foo-*', + sourcemaps: 'foo-*', + errors: 'foo-*', + onboarding: 'foo-*', + spans: 'foo-*', + transactions: 'foo-*', + metrics: 'foo-*', }, headers: { 'kbn-xsrf': true, @@ -50,12 +49,12 @@ describe('No data screen', () => { url: apmIndicesSaveURL, method: 'POST', body: { - 'apm_oss.sourcemapIndices': '', - 'apm_oss.errorIndices': '', - 'apm_oss.onboardingIndices': '', - 'apm_oss.spanIndices': '', - 'apm_oss.transactionIndices': '', - 'apm_oss.metricsIndices': '', + sourcemaps: '', + errors: '', + onboarding: '', + spans: '', + transactions: '', + metrics: '', }, headers: { 'kbn-xsrf': true }, auth: { user: 'apm_power_user', pass: 'changeme' }, diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 4e82d82d655b4..865358959ea72 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -8,7 +8,6 @@ "version": "8.0.0", "kibanaVersion": "kibana", "requiredPlugins": [ - "apmOss", "data", "embeddable", "features", diff --git a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx index 6685dddd87d7f..2e526eff04346 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx @@ -30,40 +30,40 @@ import { const APM_INDEX_LABELS = [ { - configurationName: 'apm_oss.sourcemapIndices', + configurationName: 'sourcemap', label: i18n.translate( 'xpack.apm.settings.apmIndices.sourcemapIndicesLabel', { defaultMessage: 'Sourcemap Indices' } ), }, { - configurationName: 'apm_oss.errorIndices', + configurationName: 'error', label: i18n.translate('xpack.apm.settings.apmIndices.errorIndicesLabel', { defaultMessage: 'Error Indices', }), }, { - configurationName: 'apm_oss.onboardingIndices', + configurationName: 'onboarding', label: i18n.translate( 'xpack.apm.settings.apmIndices.onboardingIndicesLabel', { defaultMessage: 'Onboarding Indices' } ), }, { - configurationName: 'apm_oss.spanIndices', + configurationName: 'span', label: i18n.translate('xpack.apm.settings.apmIndices.spanIndicesLabel', { defaultMessage: 'Span Indices', }), }, { - configurationName: 'apm_oss.transactionIndices', + configurationName: 'transaction', label: i18n.translate( 'xpack.apm.settings.apmIndices.transactionIndicesLabel', { defaultMessage: 'Transaction Indices' } ), }, { - configurationName: 'apm_oss.metricsIndices', + configurationName: 'metric', label: i18n.translate('xpack.apm.settings.apmIndices.metricsIndicesLabel', { defaultMessage: 'Metrics Indices', }), @@ -145,7 +145,7 @@ export function ApmIndices() { } ), }); - } catch (error) { + } catch (error: any) { notifications.toasts.addDanger({ title: i18n.translate( 'xpack.apm.settings.apmIndices.applyChanges.failed.title', @@ -215,7 +215,10 @@ export function ApmIndices() { { defaultMessage: 'Overrides {configurationName}: {defaultValue}', - values: { configurationName, defaultValue }, + values: { + configurationName: `xpack.apm.indices.${configurationName}`, + defaultValue, + }, } )} fullWidth diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx index 8764ac48c5440..2203bc63f68cd 100644 --- a/x-pack/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -119,14 +119,12 @@ interface MockSetup { config: APMConfig; uiFilters: UxUIFilters; indices: { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': string; - 'apm_oss.errorIndices': string; - 'apm_oss.onboardingIndices': string; - 'apm_oss.spanIndices': string; - 'apm_oss.transactionIndices': string; - 'apm_oss.metricsIndices': string; - /* eslint-enable @typescript-eslint/naming-convention */ + sourcemaps: string; + errors: string; + onboarding: string; + spans: string; + transactions: string; + metrics: string; apmAgentConfigurationIndex: string; apmCustomLinkIndex: string; }; @@ -178,14 +176,12 @@ export async function inspectSearchParams( ) as APMConfig, uiFilters: {}, indices: { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': 'myIndex', - 'apm_oss.errorIndices': 'myIndex', - 'apm_oss.onboardingIndices': 'myIndex', - 'apm_oss.spanIndices': 'myIndex', - 'apm_oss.transactionIndices': 'myIndex', - 'apm_oss.metricsIndices': 'myIndex', - /* eslint-enable @typescript-eslint/naming-convention */ + sourcemaps: 'myIndex', + errors: 'myIndex', + onboarding: 'myIndex', + spans: 'myIndex', + transactions: 'myIndex', + metrics: 'myIndex', apmAgentConfigurationIndex: 'myIndex', apmCustomLinkIndex: 'myIndex', }, diff --git a/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts b/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts index a85bd007bc4f3..f3e2b48390468 100644 --- a/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts +++ b/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts @@ -38,14 +38,12 @@ export const readKibanaConfig = () => { }; return { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.transactionIndices': 'apm-*', - 'apm_oss.metricsIndices': 'apm-*', - 'apm_oss.errorIndices': 'apm-*', - 'apm_oss.spanIndices': 'apm-*', - 'apm_oss.onboardingIndices': 'apm-*', - 'apm_oss.sourcemapIndices': 'apm-*', - /* eslint-enable @typescript-eslint/naming-convention */ + 'xpack.apm.indices.transaction': 'traces-apm*,apm-*', + 'xpack.apm.indices.metric': 'metrics-apm*,apm-*', + 'xpack.apm.indices.error': 'logs-apm*,apm-*', + 'xpack.apm.indices.span': 'traces-apm*,apm-*', + 'xpack.apm.indices.onboarding': 'apm-*', + 'xpack.apm.indices.sourcemap': 'apm-*', 'elasticsearch.hosts': 'http://localhost:9200', ...loadedKibanaConfig, ...cliEsCredentials, diff --git a/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts b/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts index 0dab75cfba9c7..c900123c6cee9 100644 --- a/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts +++ b/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts @@ -78,7 +78,12 @@ async function uploadData() { collectTelemetryParams: { logger: console as unknown as Logger, indices: { - ...config, + transaction: config['xpack.apm.indices.transaction'], + metric: config['xpack.apm.indices.metric'], + error: config['xpack.apm.indices.error'], + span: config['xpack.apm.indices.span'], + onboarding: config['xpack.apm.indices.onboarding'], + sourcemap: config['xpack.apm.indices.sourcemap'], apmCustomLinkIndex: '.apm-custom-links', apmAgentConfigurationIndex: '.apm-agent-configuration', }, diff --git a/x-pack/plugins/apm/server/index.test.ts b/x-pack/plugins/apm/server/index.test.ts deleted file mode 100644 index be93557fea6fc..0000000000000 --- a/x-pack/plugins/apm/server/index.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { APMOSSConfig } from 'src/plugins/apm_oss/server'; -import { APMXPackConfig } from '.'; -import { mergeConfigs } from './index'; - -describe('mergeConfigs', () => { - it('merges the configs', () => { - const apmOssConfig = { - transactionIndices: 'apm-*-transaction-*', - spanIndices: 'apm-*-span-*', - errorIndices: 'apm-*-error-*', - metricsIndices: 'apm-*-metric-*', - } as APMOSSConfig; - - const apmConfig = { - ui: { enabled: false }, - enabled: true, - metricsInterval: 2000, - agent: { migrations: { enabled: true } }, - } as APMXPackConfig; - - expect(mergeConfigs(apmOssConfig, apmConfig)).toEqual({ - 'apm_oss.errorIndices': 'logs-apm*,apm-*-error-*', - 'apm_oss.metricsIndices': 'metrics-apm*,apm-*-metric-*', - 'apm_oss.spanIndices': 'traces-apm*,apm-*-span-*', - 'apm_oss.transactionIndices': 'traces-apm*,apm-*-transaction-*', - 'xpack.apm.metricsInterval': 2000, - 'xpack.apm.ui.enabled': false, - 'xpack.apm.agent.migrations.enabled': true, - }); - }); -}); diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts index 22787b0301ce0..c0dffc50e4e4f 100644 --- a/x-pack/plugins/apm/server/index.ts +++ b/x-pack/plugins/apm/server/index.ts @@ -10,11 +10,12 @@ import { PluginConfigDescriptor, PluginInitializerContext, } from 'src/core/server'; -import { APMOSSConfig } from 'src/plugins/apm_oss/server'; import { maxSuggestions } from '../../observability/common'; import { SearchAggregatedTransactionSetting } from '../common/aggregated_transactions'; import { APMPlugin } from './plugin'; +// All options should be documented in the APM configuration settings: https://github.com/elastic/kibana/blob/master/docs/settings/apm-settings.asciidoc +// and be included on cloud allow list unless there are specific reasons not to const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), serviceMapEnabled: schema.boolean({ defaultValue: true }), @@ -47,12 +48,37 @@ const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: false }), }), }), + indices: schema.object({ + transaction: schema.string({ defaultValue: 'traces-apm*,apm-*' }), + span: schema.string({ defaultValue: 'traces-apm*,apm-*' }), + error: schema.string({ defaultValue: 'logs-apm*,apm-*' }), + metric: schema.string({ defaultValue: 'metrics-apm*,apm-*' }), + sourcemap: schema.string({ defaultValue: 'apm-*' }), + onboarding: schema.string({ defaultValue: 'apm-*' }), + }), }); // plugin config -export const config: PluginConfigDescriptor = { - deprecations: ({ deprecate, renameFromRoot }) => [ +export const config: PluginConfigDescriptor = { + deprecations: ({ + deprecate, + renameFromRoot, + deprecateFromRoot, + unusedFromRoot, + }) => [ deprecate('enabled', '8.0.0'), + renameFromRoot( + 'apm_oss.transactionIndices', + 'xpack.apm.indices.transaction' + ), + renameFromRoot('apm_oss.spanIndices', 'xpack.apm.indices.span'), + renameFromRoot('apm_oss.errorIndices', 'xpack.apm.indices.error'), + renameFromRoot('apm_oss.metricsIndices', 'xpack.apm.indices.metric'), + renameFromRoot('apm_oss.sourcemapIndices', 'xpack.apm.indices.sourcemap'), + renameFromRoot('apm_oss.onboardingIndices', 'xpack.apm.indices.onboarding'), + deprecateFromRoot('apm_oss.enabled', '8.0.0'), + unusedFromRoot('apm_oss.fleetMode'), + unusedFromRoot('apm_oss.indexPattern'), renameFromRoot( 'xpack.apm.maxServiceEnvironments', `uiSettings.overrides[${maxSuggestions}]` @@ -70,69 +96,8 @@ export const config: PluginConfigDescriptor = { schema: configSchema, }; -export type APMXPackConfig = TypeOf; -export type APMConfig = ReturnType; - -// plugin config and ui indices settings -// All options should be documented in the APM configuration settings: https://github.com/elastic/kibana/blob/master/docs/settings/apm-settings.asciidoc -// and be included on cloud allow list unless there are specific reasons not to -export function mergeConfigs( - apmOssConfig: APMOSSConfig, - apmConfig: APMXPackConfig -) { - const mergedConfig = { - /* eslint-disable @typescript-eslint/naming-convention */ - // TODO: Remove all apm_oss options by 8.0 - 'apm_oss.transactionIndices': apmOssConfig.transactionIndices, - 'apm_oss.spanIndices': apmOssConfig.spanIndices, - 'apm_oss.errorIndices': apmOssConfig.errorIndices, - 'apm_oss.metricsIndices': apmOssConfig.metricsIndices, - 'apm_oss.sourcemapIndices': apmOssConfig.sourcemapIndices, - 'apm_oss.onboardingIndices': apmOssConfig.onboardingIndices, - /* eslint-enable @typescript-eslint/naming-convention */ - 'xpack.apm.serviceMapEnabled': apmConfig.serviceMapEnabled, - 'xpack.apm.serviceMapFingerprintBucketSize': - apmConfig.serviceMapFingerprintBucketSize, - 'xpack.apm.serviceMapTraceIdBucketSize': - apmConfig.serviceMapTraceIdBucketSize, - 'xpack.apm.serviceMapFingerprintGlobalBucketSize': - apmConfig.serviceMapFingerprintGlobalBucketSize, - 'xpack.apm.serviceMapTraceIdGlobalBucketSize': - apmConfig.serviceMapTraceIdGlobalBucketSize, - 'xpack.apm.serviceMapMaxTracesPerRequest': - apmConfig.serviceMapMaxTracesPerRequest, - 'xpack.apm.ui.enabled': apmConfig.ui.enabled, - 'xpack.apm.ui.maxTraceItems': apmConfig.ui.maxTraceItems, - 'xpack.apm.ui.transactionGroupBucketSize': - apmConfig.ui.transactionGroupBucketSize, - 'xpack.apm.autocreateApmIndexPattern': apmConfig.autocreateApmIndexPattern, - 'xpack.apm.telemetryCollectionEnabled': - apmConfig.telemetryCollectionEnabled, - 'xpack.apm.searchAggregatedTransactions': - apmConfig.searchAggregatedTransactions, - 'xpack.apm.metricsInterval': apmConfig.metricsInterval, - 'xpack.apm.agent.migrations.enabled': apmConfig.agent.migrations.enabled, - }; - - // Add data stream indices to list of configured values - mergedConfig[ - 'apm_oss.transactionIndices' - ] = `traces-apm*,${mergedConfig['apm_oss.transactionIndices']}`; - - mergedConfig[ - 'apm_oss.spanIndices' - ] = `traces-apm*,${mergedConfig['apm_oss.spanIndices']}`; - - mergedConfig[ - 'apm_oss.errorIndices' - ] = `logs-apm*,${mergedConfig['apm_oss.errorIndices']}`; - - mergedConfig[ - 'apm_oss.metricsIndices' - ] = `metrics-apm*,${mergedConfig['apm_oss.metricsIndices']}`; - - return mergedConfig; -} +export type APMConfig = TypeOf; +export type ApmIndicesConfigName = keyof APMConfig['indices']; export const plugin = (initContext: PluginInitializerContext) => new APMPlugin(initContext); diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts index d1026b0b6ca8b..7fe2adcfe24d7 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts @@ -99,7 +99,7 @@ export function registerErrorCountAlertType({ }); const searchParams = { - index: indices['apm_oss.errorIndices'], + index: indices.error, size: 0, body: { query: { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts index b383b4777eca4..df4de254346c9 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts @@ -115,12 +115,12 @@ export function registerTransactionDurationAlertType({ // to prevent (likely) unnecessary blocking request // in rule execution const searchAggregatedTransactions = - config['xpack.apm.searchAggregatedTransactions'] !== + config.searchAggregatedTransactions !== SearchAggregatedTransactionSetting.never; const index = searchAggregatedTransactions - ? indices['apm_oss.metricsIndices'] - : indices['apm_oss.transactionIndices']; + ? indices.metric + : indices.transaction; const field = getTransactionDurationFieldForAggregatedTransactions( searchAggregatedTransactions diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts index 6c59bcc4107b0..598487d02625a 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts @@ -110,12 +110,12 @@ export function registerTransactionErrorRateAlertType({ // to prevent (likely) unnecessary blocking request // in rule execution const searchAggregatedTransactions = - config['xpack.apm.searchAggregatedTransactions'] !== + config.searchAggregatedTransactions !== SearchAggregatedTransactionSetting.never; const index = searchAggregatedTransactions - ? indices['apm_oss.metricsIndices'] - : indices['apm_oss.transactionIndices']; + ? indices.metric + : indices.transaction; const searchParams = { index, diff --git a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts index 5d5865bdd2289..22649a7010461 100644 --- a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts +++ b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts @@ -17,10 +17,10 @@ export const createRuleTypeMocks = () => { let alertExecutor: (...args: any[]) => Promise; const mockedConfig$ = of({ - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.errorIndices': 'apm-*', - 'apm_oss.transactionIndices': 'apm-*', - /* eslint-enable @typescript-eslint/naming-convention */ + indices: { + error: 'apm-*', + transaction: 'apm-*', + }, } as APMConfig); const loggerMock = { diff --git a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts index 324202b207237..10758b6d90cdc 100644 --- a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts +++ b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts @@ -50,7 +50,7 @@ export async function createAnomalyDetectionJobs( `Creating ML anomaly detection jobs for environments: [${uniqueMlJobEnvs}].` ); - const indexPatternName = indices['apm_oss.metricsIndices']; + const indexPatternName = indices.metric; const responses = await Promise.all( uniqueMlJobEnvs.map((environment) => createAnomalyDetectionJob({ ml, environment, indexPatternName }) diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts index 4bfac442b4a3c..1e697ebdcae06 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts @@ -14,12 +14,10 @@ import { describe('data telemetry collection tasks', () => { const indices = { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.errorIndices': 'apm-8.0.0-error', - 'apm_oss.metricsIndices': 'apm-8.0.0-metric', - 'apm_oss.spanIndices': 'apm-8.0.0-span', - 'apm_oss.transactionIndices': 'apm-8.0.0-transaction', - /* eslint-enable @typescript-eslint/naming-convention */ + error: 'apm-8.0.0-error', + metric: 'apm-8.0.0-metric', + span: 'apm-8.0.0-span', + transaction: 'apm-8.0.0-transaction', } as ApmIndicesConfig; describe('environments', () => { diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index d624c8527df86..8764223ad1ebb 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -78,7 +78,7 @@ export const tasks: TelemetryTask[] = [ }; const params = { - index: [indices['apm_oss.transactionIndices']], + index: [indices.transaction], body: { size: 0, timeout, @@ -138,7 +138,7 @@ export const tasks: TelemetryTask[] = [ // fixed date range for reliable results const lastTransaction = ( await search({ - index: indices['apm_oss.transactionIndices'], + index: indices.transaction, body: { query: { bool: { @@ -253,10 +253,10 @@ export const tasks: TelemetryTask[] = [ const response = await search({ index: [ - indices['apm_oss.errorIndices'], - indices['apm_oss.metricsIndices'], - indices['apm_oss.spanIndices'], - indices['apm_oss.transactionIndices'], + indices.error, + indices.metric, + indices.span, + indices.transaction, ], body: { size: 0, @@ -310,10 +310,10 @@ export const tasks: TelemetryTask[] = [ const response = await search({ index: [ - indices['apm_oss.errorIndices'], - indices['apm_oss.metricsIndices'], - indices['apm_oss.spanIndices'], - indices['apm_oss.transactionIndices'], + indices.error, + indices.metric, + indices.span, + indices.transaction, ], body: { size: 0, @@ -345,7 +345,7 @@ export const tasks: TelemetryTask[] = [ name: 'environments', executor: async ({ indices, search }) => { const response = await search({ - index: [indices['apm_oss.transactionIndices']], + index: [indices.transaction], body: { query: { bool: { @@ -426,12 +426,12 @@ export const tasks: TelemetryTask[] = [ name: 'processor_events', executor: async ({ indices, search }) => { const indicesByProcessorEvent = { - error: indices['apm_oss.errorIndices'], - metric: indices['apm_oss.metricsIndices'], - span: indices['apm_oss.spanIndices'], - transaction: indices['apm_oss.transactionIndices'], - onboarding: indices['apm_oss.onboardingIndices'], - sourcemap: indices['apm_oss.sourcemapIndices'], + error: indices.error, + metric: indices.metric, + span: indices.span, + transaction: indices.transaction, + onboarding: indices.onboarding, + sourcemap: indices.sourcemap, }; type ProcessorEvent = keyof typeof indicesByProcessorEvent; @@ -549,10 +549,10 @@ export const tasks: TelemetryTask[] = [ return prevJob.then(async (data) => { const response = await search({ index: [ - indices['apm_oss.errorIndices'], - indices['apm_oss.spanIndices'], - indices['apm_oss.metricsIndices'], - indices['apm_oss.transactionIndices'], + indices.error, + indices.span, + indices.metric, + indices.transaction, ], body: { size: 0, @@ -598,11 +598,7 @@ export const tasks: TelemetryTask[] = [ name: 'versions', executor: async ({ search, indices }) => { const response = await search({ - index: [ - indices['apm_oss.transactionIndices'], - indices['apm_oss.spanIndices'], - indices['apm_oss.errorIndices'], - ], + index: [indices.transaction, indices.span, indices.error], terminateAfter: 1, body: { query: { @@ -647,7 +643,7 @@ export const tasks: TelemetryTask[] = [ executor: async ({ search, indices }) => { const errorGroupsCount = ( await search({ - index: indices['apm_oss.errorIndices'], + index: indices.error, body: { size: 0, timeout, @@ -683,7 +679,7 @@ export const tasks: TelemetryTask[] = [ const transactionGroupsCount = ( await search({ - index: indices['apm_oss.transactionIndices'], + index: indices.transaction, body: { size: 0, timeout, @@ -719,7 +715,7 @@ export const tasks: TelemetryTask[] = [ const tracesPerDayCount = ( await search({ - index: indices['apm_oss.transactionIndices'], + index: indices.transaction, body: { query: { bool: { @@ -741,11 +737,7 @@ export const tasks: TelemetryTask[] = [ const servicesCount = ( await search({ - index: [ - indices['apm_oss.transactionIndices'], - indices['apm_oss.errorIndices'], - indices['apm_oss.metricsIndices'], - ], + index: [indices.transaction, indices.error, indices.metric], body: { size: 0, timeout, @@ -811,11 +803,7 @@ export const tasks: TelemetryTask[] = [ const data = await prevJob; const response = await search({ - index: [ - indices['apm_oss.errorIndices'], - indices['apm_oss.metricsIndices'], - indices['apm_oss.transactionIndices'], - ], + index: [indices.error, indices.metric, indices.transaction], body: { size: 0, timeout, @@ -1006,12 +994,12 @@ export const tasks: TelemetryTask[] = [ const response = await indicesStats({ index: [ indices.apmAgentConfigurationIndex, - indices['apm_oss.errorIndices'], - indices['apm_oss.metricsIndices'], - indices['apm_oss.onboardingIndices'], - indices['apm_oss.sourcemapIndices'], - indices['apm_oss.spanIndices'], - indices['apm_oss.transactionIndices'], + indices.error, + indices.metric, + indices.onboarding, + indices.sourcemap, + indices.span, + indices.transaction, ], }); diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts index 809869e13de7f..871df10d9bafa 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts @@ -43,14 +43,12 @@ describe('get buckets', () => { } ) as APMConfig, indices: { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': 'apm-*', - 'apm_oss.errorIndices': 'apm-*', - 'apm_oss.onboardingIndices': 'apm-*', - 'apm_oss.spanIndices': 'apm-*', - 'apm_oss.transactionIndices': 'apm-*', - 'apm_oss.metricsIndices': 'apm-*', - /* eslint-enable @typescript-eslint/naming-convention */ + sourcemap: 'apm-*', + error: 'apm-*', + onboarding: 'apm-*', + span: 'apm-*', + transaction: 'apm-*', + metric: 'apm-*', apmAgentConfigurationIndex: '.apm-agent-configuration', apmCustomLinkIndex: '.apm-custom-link', }, diff --git a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.test.ts b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.test.ts index f17224384842d..1fac873ced7be 100644 --- a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.test.ts @@ -58,8 +58,7 @@ describe('getIsUsingTransactionEvents', () => { describe('with config xpack.apm.searchAggregatedTransactions: never', () => { const config = { - 'xpack.apm.searchAggregatedTransactions': - SearchAggregatedTransactionSetting.never, + searchAggregatedTransactions: SearchAggregatedTransactionSetting.never, }; it('should be false', async () => { @@ -81,8 +80,7 @@ describe('getIsUsingTransactionEvents', () => { describe('with config xpack.apm.searchAggregatedTransactions: always', () => { const config = { - 'xpack.apm.searchAggregatedTransactions': - SearchAggregatedTransactionSetting.always, + searchAggregatedTransactions: SearchAggregatedTransactionSetting.always, }; it('should be false when kuery is empty', async () => { mock = await inspectSearchParams( @@ -164,8 +162,7 @@ describe('getIsUsingTransactionEvents', () => { describe('with config xpack.apm.searchAggregatedTransactions: auto', () => { const config = { - 'xpack.apm.searchAggregatedTransactions': - SearchAggregatedTransactionSetting.auto, + searchAggregatedTransactions: SearchAggregatedTransactionSetting.auto, }; it('should query for data once if metrics data found', async () => { diff --git a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.ts b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.ts index 70df0959a63b6..66e9697ab7c91 100644 --- a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.ts +++ b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/get_is_using_transaction_events.ts @@ -23,8 +23,7 @@ export async function getIsUsingTransactionEvents({ start?: number; end?: number; }): Promise { - const searchAggregatedTransactions = - config['xpack.apm.searchAggregatedTransactions']; + const searchAggregatedTransactions = config.searchAggregatedTransactions; if ( searchAggregatedTransactions === SearchAggregatedTransactionSetting.never diff --git a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts index 478f3218ef38c..a58a95dd43fcc 100644 --- a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts @@ -63,8 +63,7 @@ export async function getSearchAggregatedTransactions({ apmEventClient: APMEventClient; kuery: string; }): Promise { - const searchAggregatedTransactions = - config['xpack.apm.searchAggregatedTransactions']; + const searchAggregatedTransactions = config.searchAggregatedTransactions; if ( kuery || diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.test.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.test.ts index 4983d6d515944..5ef3786e9bde4 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ import { APMEventESSearchRequest } from '.'; import { ApmIndicesConfig } from '../../../settings/apm_indices/get_apm_indices'; import { unpackProcessorEvents } from './unpack_processor_events'; @@ -19,12 +18,12 @@ describe('unpackProcessorEvents', () => { } as APMEventESSearchRequest; const indices = { - 'apm_oss.transactionIndices': 'my-apm-*-transaction-*', - 'apm_oss.metricsIndices': 'my-apm-*-metric-*', - 'apm_oss.errorIndices': 'my-apm-*-error-*', - 'apm_oss.spanIndices': 'my-apm-*-span-*', - 'apm_oss.onboardingIndices': 'my-apm-*-onboarding-', - 'apm_oss.sourcemapIndices': 'my-apm-*-sourcemap-*', + transaction: 'my-apm-*-transaction-*', + metric: 'my-apm-*-metric-*', + error: 'my-apm-*-error-*', + span: 'my-apm-*-span-*', + onboarding: 'my-apm-*-onboarding-*', + sourcemap: 'my-apm-*-sourcemap-*', } as ApmIndicesConfig; res = unpackProcessorEvents(request, indices); diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts index 47a2b3fe7e5c8..582fe0374c5ca 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts @@ -13,19 +13,16 @@ import { ESFilter, } from '../../../../../../../../src/core/types/elasticsearch'; import { APMEventESSearchRequest, APMEventESTermsEnumRequest } from '.'; -import { - ApmIndicesConfig, - ApmIndicesName, -} from '../../../settings/apm_indices/get_apm_indices'; +import { ApmIndicesConfig } from '../../../settings/apm_indices/get_apm_indices'; -const processorEventIndexMap: Record = { - [ProcessorEvent.transaction]: 'apm_oss.transactionIndices', - [ProcessorEvent.span]: 'apm_oss.spanIndices', - [ProcessorEvent.metric]: 'apm_oss.metricsIndices', - [ProcessorEvent.error]: 'apm_oss.errorIndices', +const processorEventIndexMap = { + [ProcessorEvent.transaction]: 'transaction', + [ProcessorEvent.span]: 'span', + [ProcessorEvent.metric]: 'metric', + [ProcessorEvent.error]: 'error', // TODO: should have its own config setting - [ProcessorEvent.profile]: 'apm_oss.transactionIndices', -}; + [ProcessorEvent.profile]: 'transaction', +} as const; export function unpackProcessorEvents( request: APMEventESSearchRequest | APMEventESTermsEnumRequest, diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts index 94e88a09ea35c..d5ff97c050d9d 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts @@ -10,19 +10,20 @@ import { APMConfig } from '../..'; import { APMRouteHandlerResources } from '../../routes/typings'; import { ProcessorEvent } from '../../../common/processor_event'; import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; +import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; +import { PromiseReturnType } from '../../../../observability/typings/common'; jest.mock('../settings/apm_indices/get_apm_indices', () => ({ - getApmIndices: async () => ({ - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': 'apm-*', - 'apm_oss.errorIndices': 'apm-*', - 'apm_oss.onboardingIndices': 'apm-*', - 'apm_oss.spanIndices': 'apm-*', - 'apm_oss.transactionIndices': 'apm-*', - 'apm_oss.metricsIndices': 'apm-*', - /* eslint-enable @typescript-eslint/naming-convention */ - apmAgentConfigurationIndex: 'apm-*', - }), + getApmIndices: async () => + ({ + sourcemap: 'apm-*', + error: 'apm-*', + onboarding: 'apm-*', + span: 'apm-*', + transaction: 'apm-*', + metric: 'apm-*', + apmAgentConfigurationIndex: 'apm-*', + } as PromiseReturnType), })); jest.mock('../index_pattern/get_dynamic_index_pattern', () => ({ diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts index aae707c6e4689..83adab6ae6cbc 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ import { createStaticIndexPattern } from './create_static_index_pattern'; import { Setup } from '../helpers/setup_request'; import * as HistoricalAgentData from '../../routes/historical_data/has_historical_agent_data'; @@ -25,11 +24,11 @@ function getMockSavedObjectsClient(existingIndexPatternTitle: string) { const setup = { indices: { - 'apm_oss.transactionIndices': 'apm-*-transaction-*', - 'apm_oss.spanIndices': 'apm-*-span-*', - 'apm_oss.errorIndices': 'apm-*-error-*', - 'apm_oss.metricsIndices': 'apm-*-metrics-*', - }, + transaction: 'apm-*-transaction-*', + span: 'apm-*-span-*', + error: 'apm-*-error-*', + metric: 'apm-*-metrics-*', + } as APMConfig['indices'], } as unknown as Setup; describe('createStaticIndexPattern', () => { @@ -37,7 +36,7 @@ describe('createStaticIndexPattern', () => { const savedObjectsClient = getMockSavedObjectsClient('apm-*'); await createStaticIndexPattern({ setup, - config: { 'xpack.apm.autocreateApmIndexPattern': false } as APMConfig, + config: { autocreateApmIndexPattern: false } as APMConfig, savedObjectsClient, spaceId: 'default', }); @@ -54,7 +53,7 @@ describe('createStaticIndexPattern', () => { await createStaticIndexPattern({ setup, - config: { 'xpack.apm.autocreateApmIndexPattern': true } as APMConfig, + config: { autocreateApmIndexPattern: true } as APMConfig, savedObjectsClient, spaceId: 'default', }); @@ -71,7 +70,7 @@ describe('createStaticIndexPattern', () => { await createStaticIndexPattern({ setup, - config: { 'xpack.apm.autocreateApmIndexPattern': true } as APMConfig, + config: { autocreateApmIndexPattern: true } as APMConfig, savedObjectsClient, spaceId: 'default', }); @@ -91,9 +90,7 @@ describe('createStaticIndexPattern', () => { await createStaticIndexPattern({ setup, - config: { - 'xpack.apm.autocreateApmIndexPattern': true, - } as APMConfig, + config: { autocreateApmIndexPattern: true } as APMConfig, savedObjectsClient, spaceId: 'default', }); @@ -120,9 +117,7 @@ describe('createStaticIndexPattern', () => { await createStaticIndexPattern({ setup, - config: { - 'xpack.apm.autocreateApmIndexPattern': true, - } as APMConfig, + config: { autocreateApmIndexPattern: true } as APMConfig, savedObjectsClient, spaceId: 'default', }); diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts index 4f35e7e639151..26ae2ac337e88 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts @@ -34,7 +34,7 @@ export async function createStaticIndexPattern({ }): Promise { return withApmSpan('create_static_index_pattern', async () => { // don't autocreate APM index pattern if it's been disabled via the config - if (!config['xpack.apm.autocreateApmIndexPattern']) { + if (!config.autocreateApmIndexPattern) { return false; } diff --git a/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.test.ts b/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.test.ts index 8103630157584..8b7444ffdf6fa 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.test.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.test.ts @@ -5,18 +5,16 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ - import { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices'; import { getApmIndexPatternTitle } from './get_apm_index_pattern_title'; describe('getApmIndexPatternTitle', () => { it('returns an index pattern title by combining existing indicies', () => { const title = getApmIndexPatternTitle({ - 'apm_oss.transactionIndices': 'apm-*-transaction-*', - 'apm_oss.spanIndices': 'apm-*-span-*', - 'apm_oss.errorIndices': 'apm-*-error-*', - 'apm_oss.metricsIndices': 'apm-*-metrics-*', + transaction: 'apm-*-transaction-*', + span: 'apm-*-span-*', + error: 'apm-*-error-*', + metric: 'apm-*-metrics-*', } as ApmIndicesConfig); expect(title).toBe( 'apm-*-transaction-*,apm-*-span-*,apm-*-error-*,apm-*-metrics-*' @@ -25,10 +23,10 @@ describe('getApmIndexPatternTitle', () => { it('removes duplicates', () => { const title = getApmIndexPatternTitle({ - 'apm_oss.transactionIndices': 'apm-*', - 'apm_oss.spanIndices': 'apm-*', - 'apm_oss.errorIndices': 'apm-*', - 'apm_oss.metricsIndices': 'apm-*', + transaction: 'apm-*', + span: 'apm-*', + error: 'apm-*', + metric: 'apm-*', } as ApmIndicesConfig); expect(title).toBe('apm-*'); }); diff --git a/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.ts b/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.ts index e65f200130e9a..5e055ff1c2fdc 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.ts @@ -10,9 +10,9 @@ import { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices'; export function getApmIndexPatternTitle(apmIndicesConfig: ApmIndicesConfig) { return uniq([ - apmIndicesConfig['apm_oss.transactionIndices'], - apmIndicesConfig['apm_oss.spanIndices'], - apmIndicesConfig['apm_oss.errorIndices'], - apmIndicesConfig['apm_oss.metricsIndices'], + apmIndicesConfig.transaction, + apmIndicesConfig.span, + apmIndicesConfig.error, + apmIndicesConfig.metric, ]).join(','); } diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts index ba35836452122..06138931c004e 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts @@ -85,7 +85,7 @@ export async function fetchAndTransformGcMetrics({ date_histogram: getMetricsDateHistogramParams({ start, end, - metricsInterval: config['xpack.apm.metricsInterval'], + metricsInterval: config.metricsInterval, }), aggs: { // get the max value diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts index a3fce0368f4a5..581a0782e4d72 100644 --- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts @@ -99,7 +99,7 @@ export async function fetchAndTransformMetrics({ date_histogram: getMetricsDateHistogramParams({ start, end, - metricsInterval: config['xpack.apm.metricsInterval'], + metricsInterval: config.metricsInterval, }), aggs, }, diff --git a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts index 9409e94fa9ba9..ba35ac5c5c89c 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts @@ -56,7 +56,7 @@ export async function hasRumData({ const response = await apmEventClient.search('has_rum_data', params); return { - indices: setup.indices['apm_oss.transactionIndices']!, + indices: setup.indices.transaction, hasData: response.hits.total.value > 0, serviceName: response.aggregations?.services?.mostTraffic?.buckets?.[0]?.key, @@ -65,7 +65,7 @@ export async function hasRumData({ return { hasData: false, serviceName: undefined, - indices: setup.indices['apm_oss.transactionIndices']!, + indices: setup.indices.transaction, }; } } diff --git a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts index 02ba0a8514b62..239cf39f15ffe 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts @@ -65,7 +65,7 @@ export const failedTransactionsCorrelationsSearchServiceProvider: FailedTransact const params: FailedTransactionsCorrelationsRequestParams & SearchStrategyServerParams = { ...searchServiceParams, - index: indices['apm_oss.transactionIndices'], + index: indices.transaction, includeFrozen, }; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts b/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts index 7e420c821a746..91f4a0d3349a4 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts @@ -67,7 +67,7 @@ export const latencyCorrelationsSearchServiceProvider: LatencyCorrelationsSearch const indices = await getApmIndices(); params = { ...searchServiceParams, - index: indices['apm_oss.transactionIndices'], + index: indices.transaction, includeFrozen, }; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts index 6e03c879f9b97..8a9d04df32036 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts @@ -90,10 +90,7 @@ const clientSearchMock = ( }; const getApmIndicesMock = async () => - ({ - // eslint-disable-next-line @typescript-eslint/naming-convention - 'apm_oss.transactionIndices': 'apm-*', - } as ApmIndicesConfig); + ({ transaction: 'apm-*' } as ApmIndicesConfig); describe('APM Correlations search strategy', () => { describe('strategy interface', () => { diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index 2497a85c0c774..ae511d0fed8f8 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -53,10 +53,7 @@ async function getConnectionData({ end, }); - const chunks = chunk( - traceIds, - setup.config['xpack.apm.serviceMapMaxTracesPerRequest'] - ); + const chunks = chunk(traceIds, setup.config.serviceMapMaxTracesPerRequest); const init = { connections: [], diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts index 2129606e69fc3..afb88189a5fd2 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts @@ -70,9 +70,7 @@ describe('getServiceMapServiceNodeInfo', () => { indices: {}, start: 1593460053026000, end: 1593497863217000, - config: { - 'xpack.apm.metricsInterval': 30, - }, + config: { metricsInterval: 30 }, uiFilters: { environment: 'test environment' }, } as unknown as Setup; const serviceName = 'test service name'; diff --git a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts index c1c11f7bf639a..7e16e69498e7c 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts @@ -60,13 +60,11 @@ export async function getTraceSampleIds({ query.bool.filter.push(...environmentQuery(environment)); const fingerprintBucketSize = serviceName - ? config['xpack.apm.serviceMapFingerprintBucketSize'] - : config['xpack.apm.serviceMapFingerprintGlobalBucketSize']; - + ? config.serviceMapFingerprintBucketSize + : config.serviceMapFingerprintGlobalBucketSize; const traceIdBucketSize = serviceName - ? config['xpack.apm.serviceMapTraceIdBucketSize'] - : config['xpack.apm.serviceMapTraceIdGlobalBucketSize']; - + ? config.serviceMapTraceIdBucketSize + : config.serviceMapTraceIdGlobalBucketSize; const samplerShardSize = traceIdBucketSize * 10; const params = { @@ -137,8 +135,7 @@ export async function getTraceSampleIds({ 'get_trace_sample_ids', params ); - // make sure at least one trace per composite/connection bucket - // is queried + // make sure at least one trace per composite/connection bucket is queried const traceIdsWithPriority = tracesSampleResponse.aggregations?.connections.buckets.flatMap((bucket) => bucket.sample.trace_ids.buckets.map((sampleDocBucket, index) => ({ diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts index d5a2006060395..fbc1e2880495b 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts @@ -57,7 +57,7 @@ export async function getServiceTransactionGroups({ end: number; }) { const { apmEventClient, config } = setup; - const bucketSize = config['xpack.apm.ui.transactionGroupBucketSize']; + const bucketSize = config.ui.transactionGroupBucketSize; const field = getTransactionDurationFieldForAggregatedTransactions( searchAggregatedTransactions diff --git a/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts b/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts index 0ade96682b362..107493af1a0c0 100644 --- a/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts +++ b/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { merge } from 'lodash'; - import { SavedObjectsClient } from 'src/core/server'; import { PromiseReturnType } from '../../../../../observability/typings/common'; import { @@ -22,8 +20,6 @@ export { ApmIndicesConfig }; type ISavedObjectsClient = Pick; -export type ApmIndicesName = keyof ApmIndicesConfig; - async function getApmIndicesSavedObject( savedObjectsClient: ISavedObjectsClient ) { @@ -38,14 +34,12 @@ async function getApmIndicesSavedObject( export function getApmIndicesConfig(config: APMConfig): ApmIndicesConfig { return { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': config['apm_oss.sourcemapIndices'], - 'apm_oss.errorIndices': config['apm_oss.errorIndices'], - 'apm_oss.onboardingIndices': config['apm_oss.onboardingIndices'], - 'apm_oss.spanIndices': config['apm_oss.spanIndices'], - 'apm_oss.transactionIndices': config['apm_oss.transactionIndices'], - 'apm_oss.metricsIndices': config['apm_oss.metricsIndices'], - /* eslint-enable @typescript-eslint/naming-convention */ + sourcemap: config.indices.sourcemap, + error: config.indices.error, + onboarding: config.indices.onboarding, + span: config.indices.span, + transaction: config.indices.transaction, + metric: config.indices.metric, // system indices, not configurable apmAgentConfigurationIndex: '.apm-agent-configuration', apmCustomLinkIndex: '.apm-custom-link', @@ -64,21 +58,12 @@ export async function getApmIndices({ savedObjectsClient ); const apmIndicesConfig = getApmIndicesConfig(config); - return merge({}, apmIndicesConfig, apmIndicesSavedObject); + return { ...apmIndicesConfig, ...apmIndicesSavedObject }; } catch (error) { return getApmIndicesConfig(config); } } -const APM_UI_INDICES: ApmIndicesName[] = [ - 'apm_oss.sourcemapIndices', - 'apm_oss.errorIndices', - 'apm_oss.onboardingIndices', - 'apm_oss.spanIndices', - 'apm_oss.transactionIndices', - 'apm_oss.metricsIndices', -]; - export async function getApmIndexSettings({ context, config, @@ -88,7 +73,7 @@ export async function getApmIndexSettings({ apmIndicesSavedObject = await getApmIndicesSavedObject( context.core.savedObjects.client ); - } catch (error) { + } catch (error: any) { if (error.output && error.output.statusCode === 404) { apmIndicesSavedObject = {}; } else { @@ -97,7 +82,11 @@ export async function getApmIndexSettings({ } const apmIndicesConfig = getApmIndicesConfig(config); - return APM_UI_INDICES.map((configurationName) => ({ + const apmIndices = Object.keys(config.indices) as Array< + keyof typeof config.indices + >; + + return apmIndices.map((configurationName) => ({ configurationName, defaultValue: apmIndicesConfig[configurationName], // value defined in kibana[.dev].yml savedValue: apmIndicesSavedObject[configurationName], // value saved via Saved Objects service diff --git a/x-pack/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap index 53318fe5fe594..ea8f39f9d9b6d 100644 --- a/x-pack/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap @@ -37,7 +37,7 @@ Object { }, }, }, - "size": "myIndex", + "size": 1000, }, } `; diff --git a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts index e940100edcf52..60a28fd9abdbd 100644 --- a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts +++ b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts @@ -24,7 +24,7 @@ export async function getTraceItems( end: number ) { const { apmEventClient, config } = setup; - const maxTraceItems = config['xpack.apm.ui.maxTraceItems']; + const maxTraceItems = config.ui.maxTraceItems; const excludedLogLevels = ['debug', 'info', 'warning']; const errorResponsePromise = apmEventClient.search('get_errors_docs', { @@ -80,9 +80,5 @@ export async function getTraceItems( const traceDocs = traceResponse.hits.hits.map((hit) => hit._source); const errorDocs = errorResponse.hits.hits.map((hit) => hit._source); - return { - exceedsMax, - traceDocs, - errorDocs, - }; + return { exceedsMax, traceDocs, errorDocs }; } diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts index 6e9d0aad96b71..76cabd3e3af93 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts @@ -11,16 +11,15 @@ import noDataResponse from './mock_responses/no_data.json'; import dataResponse from './mock_responses/data.json'; import { APMConfig } from '../../..'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; - -const mockIndices = { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': 'myIndex', - 'apm_oss.errorIndices': 'myIndex', - 'apm_oss.onboardingIndices': 'myIndex', - 'apm_oss.spanIndices': 'myIndex', - 'apm_oss.transactionIndices': 'myIndex', - 'apm_oss.metricsIndices': 'myIndex', - /* eslint-enable @typescript-eslint/naming-convention */ +import { ApmIndicesConfig } from '../../settings/apm_indices/get_apm_indices'; + +const mockIndices: ApmIndicesConfig = { + sourcemap: 'myIndex', + error: 'myIndex', + onboarding: 'myIndex', + span: 'myIndex', + transaction: 'myIndex', + metric: 'myIndex', apmAgentConfigurationIndex: 'myIndex', apmCustomLinkIndex: 'myIndex', }; diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts index 62277dba8ac29..a5c11776c70b0 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts @@ -124,7 +124,7 @@ export async function getTransactionBreakdown({ date_histogram: getMetricsDateHistogramParams({ start, end, - metricsInterval: config['xpack.apm.metricsInterval'], + metricsInterval: config.metricsInterval, }), aggs: subAggs, }, diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 2296227de2a33..d2d8dbf602364 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { combineLatest } from 'rxjs'; -import { map, take } from 'rxjs/operators'; +import { take } from 'rxjs/operators'; import { CoreSetup, CoreStart, @@ -19,8 +18,7 @@ import { isEmpty, mapValues } from 'lodash'; import { SavedObjectsClient } from '../../../../src/core/server'; import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map'; import { Dataset } from '../../rule_registry/server'; -import { APMConfig, APMXPackConfig, APM_SERVER_FEATURE_ID } from '.'; -import { mergeConfigs } from './index'; +import { APMConfig, APM_SERVER_FEATURE_ID } from '.'; import { UI_SETTINGS } from '../../../../src/plugins/data/common'; import { APM_FEATURE, registerFeaturesUsage } from './feature'; import { registerApmAlerts } from './lib/alerts/register_apm_alerts'; @@ -73,29 +71,23 @@ export class APMPlugin plugins: Omit ) { this.logger = this.initContext.logger.get(); - const config$ = this.initContext.config.create(); - const mergedConfig$ = combineLatest(plugins.apmOss.config$, config$).pipe( - map(([apmOssConfig, apmConfig]) => mergeConfigs(apmOssConfig, apmConfig)) - ); + const config$ = this.initContext.config.create(); core.savedObjects.registerType(apmIndices); core.savedObjects.registerType(apmTelemetry); core.savedObjects.registerType(apmServerSettings); - const currentConfig = mergeConfigs( - plugins.apmOss.config, - this.initContext.config.get() - ); + const currentConfig = this.initContext.config.get(); this.currentConfig = currentConfig; if ( plugins.taskManager && plugins.usageCollection && - currentConfig['xpack.apm.telemetryCollectionEnabled'] + currentConfig.telemetryCollectionEnabled ) { createApmTelemetry({ core, - config$: mergedConfig$, + config$, usageCollector: plugins.usageCollection, taskManager: plugins.taskManager, logger: this.logger, @@ -156,7 +148,7 @@ export class APMPlugin const boundGetApmIndices = async () => getApmIndices({ savedObjectsClient: await getInternalSavedObjectsClient(core), - config: await mergedConfig$.pipe(take(1)).toPromise(), + config: await config$.pipe(take(1)).toPromise(), }); boundGetApmIndices().then((indices) => { @@ -193,7 +185,7 @@ export class APMPlugin ruleDataClient, alerting: plugins.alerting, ml: plugins.ml, - config$: mergedConfig$, + config$, logger: this.logger!.get('rule'), }); } @@ -231,7 +223,7 @@ export class APMPlugin }); return { - config$: mergedConfig$, + config$, getApmIndices: boundGetApmIndices, createApmEventClient: async ({ request, diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts index d8097228df0dc..2884c08ceb9a1 100644 --- a/x-pack/plugins/apm/server/routes/fleet.ts +++ b/x-pack/plugins/apm/server/routes/fleet.ts @@ -129,8 +129,7 @@ const getMigrationCheckRoute = createApmServerRoute({ options: { tags: ['access:apm'] }, handler: async (resources) => { const { plugins, context, config, request } = resources; - const cloudApmMigrationEnabled = - config['xpack.apm.agent.migrations.enabled']; + const cloudApmMigrationEnabled = config.agent.migrations.enabled; if (!plugins.fleet || !plugins.security) { throw Boom.internal(FLEET_SECURITY_REQUIRED_MESSAGE); } @@ -158,8 +157,7 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({ options: { tags: ['access:apm', 'access:apm_write'] }, handler: async (resources) => { const { plugins, context, config, request, logger } = resources; - const cloudApmMigrationEnabled = - config['xpack.apm.agent.migrations.enabled']; + const cloudApmMigrationEnabled = config.agent.migrations.enabled; if (!plugins.fleet || !plugins.security) { throw Boom.internal(FLEET_SECURITY_REQUIRED_MESSAGE); } diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts index f9062ac13e049..17fb9d7c98c5f 100644 --- a/x-pack/plugins/apm/server/routes/service_map.ts +++ b/x-pack/plugins/apm/server/routes/service_map.ts @@ -33,7 +33,7 @@ const serviceMapRoute = createApmServerRoute({ options: { tags: ['access:apm'] }, handler: async (resources) => { const { config, context, params, logger } = resources; - if (!config['xpack.apm.serviceMapEnabled']) { + if (!config.serviceMapEnabled) { throw Boom.notFound(); } if (!isActivePlatinumLicense(context.licensing.license)) { @@ -81,7 +81,7 @@ const serviceMapServiceNodeRoute = createApmServerRoute({ handler: async (resources) => { const { config, context, params } = resources; - if (!config['xpack.apm.serviceMapEnabled']) { + if (!config.serviceMapEnabled) { throw Boom.notFound(); } if (!isActivePlatinumLicense(context.licensing.license)) { @@ -125,7 +125,7 @@ const serviceMapBackendNodeRoute = createApmServerRoute({ handler: async (resources) => { const { config, context, params } = resources; - if (!config['xpack.apm.serviceMapEnabled']) { + if (!config.serviceMapEnabled) { throw Boom.notFound(); } if (!isActivePlatinumLicense(context.licensing.license)) { diff --git a/x-pack/plugins/apm/server/routes/settings/apm_indices.ts b/x-pack/plugins/apm/server/routes/settings/apm_indices.ts index 1cba5f972c27e..156f4d1af0bb2 100644 --- a/x-pack/plugins/apm/server/routes/settings/apm_indices.ts +++ b/x-pack/plugins/apm/server/routes/settings/apm_indices.ts @@ -13,6 +13,7 @@ import { getApmIndexSettings, } from '../../lib/settings/apm_indices/get_apm_indices'; import { saveApmIndices } from '../../lib/settings/apm_indices/save_apm_indices'; +import { APMConfig } from '../..'; // get list of apm indices and values const apmIndexSettingsRoute = createApmServerRoute({ @@ -37,6 +38,10 @@ const apmIndicesRoute = createApmServerRoute({ }, }); +type SaveApmIndicesBodySchema = { + [Property in keyof APMConfig['indices']]: t.StringC; +}; + // save ui indices const saveApmIndicesRoute = createApmServerRoute({ endpoint: 'POST /internal/apm/settings/apm-indices/save', @@ -45,15 +50,13 @@ const saveApmIndicesRoute = createApmServerRoute({ }, params: t.type({ body: t.partial({ - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': t.string, - 'apm_oss.errorIndices': t.string, - 'apm_oss.onboardingIndices': t.string, - 'apm_oss.spanIndices': t.string, - 'apm_oss.transactionIndices': t.string, - 'apm_oss.metricsIndices': t.string, - /* eslint-enable @typescript-eslint/naming-convention */ - }), + sourcemap: t.string, + error: t.string, + onboarding: t.string, + span: t.string, + transaction: t.string, + metric: t.string, + } as SaveApmIndicesBodySchema), }), handler: async (resources) => { const { params, context } = resources; diff --git a/x-pack/plugins/apm/server/saved_objects/apm_indices.ts b/x-pack/plugins/apm/server/saved_objects/apm_indices.ts index df5267023ae89..4aa6c4953056a 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_indices.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_indices.ts @@ -7,34 +7,24 @@ import { SavedObjectsType } from 'src/core/server'; import { i18n } from '@kbn/i18n'; +import { updateApmOssIndexPaths } from './migrations/update_apm_oss_index_paths'; +import { ApmIndicesConfigName } from '..'; + +const properties: { [Property in ApmIndicesConfigName]: { type: 'keyword' } } = + { + sourcemap: { type: 'keyword' }, + error: { type: 'keyword' }, + onboarding: { type: 'keyword' }, + span: { type: 'keyword' }, + transaction: { type: 'keyword' }, + metric: { type: 'keyword' }, + }; export const apmIndices: SavedObjectsType = { name: 'apm-indices', hidden: false, namespaceType: 'agnostic', - mappings: { - properties: { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': { - type: 'keyword', - }, - 'apm_oss.errorIndices': { - type: 'keyword', - }, - 'apm_oss.onboardingIndices': { - type: 'keyword', - }, - 'apm_oss.spanIndices': { - type: 'keyword', - }, - 'apm_oss.transactionIndices': { - type: 'keyword', - }, - 'apm_oss.metricsIndices': { - type: 'keyword', - }, - }, - }, + mappings: { properties }, management: { importableAndExportable: true, icon: 'apmApp', @@ -43,4 +33,10 @@ export const apmIndices: SavedObjectsType = { defaultMessage: 'APM Settings - Index', }), }, + migrations: { + '7.16.0': (doc) => { + const attributes = updateApmOssIndexPaths(doc.attributes); + return { ...doc, attributes }; + }, + }, }; diff --git a/x-pack/plugins/apm/server/saved_objects/migrations/update_apm_oss_index_paths.ts b/x-pack/plugins/apm/server/saved_objects/migrations/update_apm_oss_index_paths.ts new file mode 100644 index 0000000000000..72ba40db0ce05 --- /dev/null +++ b/x-pack/plugins/apm/server/saved_objects/migrations/update_apm_oss_index_paths.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +const apmIndexConfigs = [ + ['sourcemap', 'apm_oss.sourcemapIndices'], + ['error', 'apm_oss.errorIndices'], + ['onboarding', 'apm_oss.onboardingIndices'], + ['span', 'apm_oss.spanIndices'], + ['transaction', 'apm_oss.transactionIndices'], + ['metric', 'apm_oss.metricsIndices'], +] as const; + +type ApmIndexConfigs = typeof apmIndexConfigs[number][0]; +type ApmIndicesSavedObjectAttributes = Partial<{ + [Property in ApmIndexConfigs]: string; +}>; +type DeprecatedApmIndexConfigPaths = typeof apmIndexConfigs[number][1]; +type DeprecatedApmIndicesSavedObjectAttributes = Partial<{ + [Property in DeprecatedApmIndexConfigPaths]: string; +}>; + +export function updateApmOssIndexPaths( + attributes: DeprecatedApmIndicesSavedObjectAttributes +) { + return apmIndexConfigs.reduce((attrs, [configPath, deprecatedConfigPath]) => { + const indexConfig: string | undefined = attributes[deprecatedConfigPath]; + if (indexConfig) { + attrs[configPath] = indexConfig; + } + return attrs; + }, {} as ApmIndicesSavedObjectAttributes); +} diff --git a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts index acc0ce69e0e4e..ba99b0624c441 100644 --- a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts +++ b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts @@ -37,13 +37,7 @@ export function onPremInstructions({ apmConfig, isFleetPluginEnabled, }: { - apmConfig: Pick< - APMConfig, - | 'apm_oss.errorIndices' - | 'apm_oss.transactionIndices' - | 'apm_oss.metricsIndices' - | 'apm_oss.onboardingIndices' - >; + apmConfig: APMConfig; isFleetPluginEnabled: boolean; }): InstructionsSchema { const EDIT_CONFIG = createEditConfig(); @@ -149,7 +143,7 @@ export function onPremInstructions({ } ), esHitsCheck: { - index: apmConfig['apm_oss.onboardingIndices'], + index: apmConfig.indices.onboarding, query: { bool: { filter: [ @@ -242,9 +236,9 @@ export function onPremInstructions({ ), esHitsCheck: { index: [ - apmConfig['apm_oss.errorIndices'], - apmConfig['apm_oss.transactionIndices'], - apmConfig['apm_oss.metricsIndices'], + apmConfig.indices.error, + apmConfig.indices.transaction, + apmConfig.indices.metric, ], query: { bool: { diff --git a/x-pack/plugins/apm/server/tutorial/index.ts b/x-pack/plugins/apm/server/tutorial/index.ts index 66e6ffaed95a8..5caf2b4372483 100644 --- a/x-pack/plugins/apm/server/tutorial/index.ts +++ b/x-pack/plugins/apm/server/tutorial/index.ts @@ -67,7 +67,7 @@ export const tutorialProvider = ], }; - if (apmConfig['xpack.apm.ui.enabled']) { + if (apmConfig.ui.enabled) { // @ts-expect-error artifacts.application is readonly artifacts.application = { path: '/app/apm', diff --git a/x-pack/plugins/apm/server/types.ts b/x-pack/plugins/apm/server/types.ts index 325891d8c1d33..c686c42beb6ef 100644 --- a/x-pack/plugins/apm/server/types.ts +++ b/x-pack/plugins/apm/server/types.ts @@ -16,7 +16,6 @@ import { PluginStart as DataPluginStart, } from '../../../../src/plugins/data/server'; import { SpacesPluginSetup, SpacesPluginStart } from '../../spaces/server'; -import { APMOSSPluginSetup } from '../../../../src/plugins/apm_oss/server'; import { HomeServerPluginSetup, HomeServerPluginStart, @@ -71,10 +70,6 @@ interface DependencyMap { setup: SpacesPluginSetup; start: SpacesPluginStart; }; - apmOss: { - setup: APMOSSPluginSetup; - start: undefined; - }; home: { setup: HomeServerPluginSetup; start: HomeServerPluginStart; @@ -135,7 +130,6 @@ interface DependencyMap { const requiredDependencies = [ 'features', - 'apmOss', 'data', 'licensing', 'triggersActionsUi', diff --git a/x-pack/plugins/apm/server/utils/test_helpers.tsx b/x-pack/plugins/apm/server/utils/test_helpers.tsx index 7b6b549e07c8d..5cf5016aec2e9 100644 --- a/x-pack/plugins/apm/server/utils/test_helpers.tsx +++ b/x-pack/plugins/apm/server/utils/test_helpers.tsx @@ -12,6 +12,7 @@ import { ESSearchResponse, } from '../../../../../src/core/types/elasticsearch'; import { UxUIFilters } from '../../typings/ui_filters'; +import { ApmIndicesConfig } from '../lib/settings/apm_indices/get_apm_indices'; interface Options { mockResponse?: ( @@ -26,18 +27,7 @@ interface MockSetup { internalClient: any; config: APMConfig; uiFilters: UxUIFilters; - indices: { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': string; - 'apm_oss.errorIndices': string; - 'apm_oss.onboardingIndices': string; - 'apm_oss.spanIndices': string; - 'apm_oss.transactionIndices': string; - 'apm_oss.metricsIndices': string; - /* eslint-enable @typescript-eslint/naming-convention */ - apmAgentConfigurationIndex: string; - apmCustomLinkIndex: string; - }; + indices: ApmIndicesConfig; } export async function inspectSearchParams( @@ -61,6 +51,16 @@ export async function inspectSearchParams( let response; let error; + const mockApmIndices: { + [Property in keyof APMConfig['indices']]: string; + } = { + sourcemap: 'myIndex', + error: 'myIndex', + onboarding: 'myIndex', + span: 'myIndex', + transaction: 'myIndex', + metric: 'myIndex', + }; const mockSetup = { apmEventClient: { search: spy } as any, internalClient: { search: spy } as any, @@ -76,8 +76,15 @@ export async function inspectSearchParams( switch (key) { default: return 'myIndex'; - - case 'xpack.apm.metricsInterval': + case 'indices': + return mockApmIndices; + case 'ui': + return { + enabled: true, + transactionGroupBucketSize: 1000, + maxTraceItems: 1000, + }; + case 'metricsInterval': return 30; } }, @@ -85,14 +92,7 @@ export async function inspectSearchParams( ) as APMConfig, uiFilters: options?.uiFilters ?? {}, indices: { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': 'myIndex', - 'apm_oss.errorIndices': 'myIndex', - 'apm_oss.onboardingIndices': 'myIndex', - 'apm_oss.spanIndices': 'myIndex', - 'apm_oss.transactionIndices': 'myIndex', - 'apm_oss.metricsIndices': 'myIndex', - /* eslint-enable @typescript-eslint/naming-convention */ + ...mockApmIndices, apmAgentConfigurationIndex: 'myIndex', apmCustomLinkIndex: 'myIndex', }, diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index c1030d2a4be1d..5db20725dd785 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -19,7 +19,6 @@ ], "references": [ { "path": "../../../src/core/tsconfig.json" }, - { "path": "../../../src/plugins/apm_oss/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../../../src/plugins/embeddable/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" }, diff --git a/x-pack/plugins/observability/common/typings.ts b/x-pack/plugins/observability/common/typings.ts index 71337075e1617..bccb0f4491009 100644 --- a/x-pack/plugins/observability/common/typings.ts +++ b/x-pack/plugins/observability/common/typings.ts @@ -16,14 +16,12 @@ export const alertWorkflowStatusRt = t.keyof({ export type AlertWorkflowStatus = t.TypeOf; export interface ApmIndicesConfig { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': string; - 'apm_oss.errorIndices': string; - 'apm_oss.onboardingIndices': string; - 'apm_oss.spanIndices': string; - 'apm_oss.transactionIndices': string; - 'apm_oss.metricsIndices': string; - /* eslint-enable @typescript-eslint/naming-convention */ + sourcemap: string; + error: string; + onboarding: string; + span: string; + transaction: string; + metric: string; apmAgentConfigurationIndex: string; apmCustomLinkIndex: string; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx index 83a7ac1ae17dc..8a766075ef8d2 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx @@ -65,7 +65,7 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { case 'mobile': const resultApm = await getDataHandler('apm')?.hasData(); hasDataT = Boolean(resultApm?.hasData); - indices = resultApm?.indices['apm_oss.transactionIndices']; + indices = resultApm?.indices.transaction; break; } setHasAppData((prevState) => ({ ...prevState, [dataType]: hasDataT })); diff --git a/x-pack/plugins/observability/public/context/has_data_context.test.tsx b/x-pack/plugins/observability/public/context/has_data_context.test.tsx index 36f7bfb990222..a586a8bf0bcce 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.test.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.test.tsx @@ -24,10 +24,7 @@ import { act } from '@testing-library/react'; const relativeStart = '2020-10-08T06:00:00.000Z'; const relativeEnd = '2020-10-08T07:00:00.000Z'; -const sampleAPMIndices = { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'apm_oss.transactionIndices': 'apm-*', -} as ApmIndicesConfig; +const sampleAPMIndices = { transaction: 'apm-*' } as ApmIndicesConfig; function wrapper({ children }: { children: React.ReactElement }) { const history = createMemoryHistory(); diff --git a/x-pack/plugins/observability/public/data_handler.test.ts b/x-pack/plugins/observability/public/data_handler.test.ts index e0b8494d4dbed..2beae5d111f7d 100644 --- a/x-pack/plugins/observability/public/data_handler.test.ts +++ b/x-pack/plugins/observability/public/data_handler.test.ts @@ -9,10 +9,7 @@ import { registerDataHandler, getDataHandler } from './data_handler'; import moment from 'moment'; import { ApmIndicesConfig } from '../common/typings'; -const sampleAPMIndices = { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'apm_oss.transactionIndices': 'apm-*', -} as ApmIndicesConfig; +const sampleAPMIndices = { transaction: 'apm-*' } as ApmIndicesConfig; const params = { absoluteTime: { diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index 3c389a0ee20b2..506b919b16416 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -36,10 +36,7 @@ function unregisterAll() { unregisterDataHandler({ appName: 'synthetics' }); } -const sampleAPMIndices = { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'apm_oss.transactionIndices': 'apm-*', -} as ApmIndicesConfig; +const sampleAPMIndices = { transaction: 'apm-*' } as ApmIndicesConfig; const withCore = makeDecorator({ name: 'withCore', diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json index a083c8f7e3bcf..daa99c4d71967 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json @@ -189,27 +189,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json index 22b79eb9bbd8e..4e41d8cb72fb5 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json @@ -216,27 +216,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json index 74083cbfa949a..9164430730216 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json @@ -214,27 +214,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/action_task_params/mappings.json b/x-pack/test/functional/es_archives/action_task_params/mappings.json index 2bb6be179d890..874886647e6d6 100644 --- a/x-pack/test/functional/es_archives/action_task_params/mappings.json +++ b/x-pack/test/functional/es_archives/action_task_params/mappings.json @@ -206,27 +206,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/actions/mappings.json b/x-pack/test/functional/es_archives/actions/mappings.json index eeb9d09d3b0a2..786005d1ab6a6 100644 --- a/x-pack/test/functional/es_archives/actions/mappings.json +++ b/x-pack/test/functional/es_archives/actions/mappings.json @@ -202,27 +202,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json index 69edf30c1ffd0..9c856a829a343 100644 --- a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json +++ b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json @@ -198,27 +198,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/canvas/reports/mappings.json b/x-pack/test/functional/es_archives/canvas/reports/mappings.json index 047a52aaa1f98..51c48857d2481 100644 --- a/x-pack/test/functional/es_archives/canvas/reports/mappings.json +++ b/x-pack/test/functional/es_archives/canvas/reports/mappings.json @@ -229,27 +229,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json index c7e1a180e6c54..bd0b2c4e9ad27 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json @@ -199,27 +199,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json index bc719ab40aebd..2da75330be93a 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json @@ -254,27 +254,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, @@ -2720,4 +2716,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json index 88f3e5d78cb16..62fec329aa40f 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json @@ -260,27 +260,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, @@ -2864,4 +2860,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json index c6b71a2613859..8a9c1a626e652 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json @@ -261,27 +261,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, @@ -2951,4 +2947,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.16.0_space/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.16.0_space/mappings.json index 51fe50218b1f5..4b8f2c7103b20 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.16.0_space/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.16.0_space/mappings.json @@ -275,27 +275,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/data/search_sessions/mappings.json b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json index a3eab7787c3eb..1203e1d892d56 100644 --- a/x-pack/test/functional/es_archives/data/search_sessions/mappings.json +++ b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json @@ -169,27 +169,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json index 4dcb8f905b197..daab89b69483e 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json @@ -198,27 +198,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json index d6d91281151aa..c133c3fec76e2 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json @@ -199,27 +199,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json index d6d91281151aa..c133c3fec76e2 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json @@ -199,27 +199,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json index d6d91281151aa..c133c3fec76e2 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json @@ -199,27 +199,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json index 4dcb8f905b197..daab89b69483e 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json @@ -198,27 +198,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json index 4dcb8f905b197..daab89b69483e 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json @@ -198,27 +198,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json index 4dcb8f905b197..daab89b69483e 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json @@ -198,27 +198,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/fleet/agents/mappings.json b/x-pack/test/functional/es_archives/fleet/agents/mappings.json index 8e4c7a912b75a..b2f5392ebd23a 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/mappings.json +++ b/x-pack/test/functional/es_archives/fleet/agents/mappings.json @@ -189,27 +189,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json b/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json index 7479c525f6a07..7853368bc37d5 100644 --- a/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json +++ b/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json @@ -181,27 +181,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/lists/mappings.json b/x-pack/test/functional/es_archives/lists/mappings.json index 134ed28bac2e7..e23c5ad224506 100644 --- a/x-pack/test/functional/es_archives/lists/mappings.json +++ b/x-pack/test/functional/es_archives/lists/mappings.json @@ -196,27 +196,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json b/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json index 5bffb9540e983..a35e4c8e07e97 100644 --- a/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json @@ -193,27 +193,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json index 254184cbef584..9d11349819d68 100644 --- a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json @@ -214,27 +214,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json b/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json index 1fdde9d9d208b..02a212d65cc1a 100644 --- a/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json @@ -170,27 +170,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json b/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json index f950877aea332..f6b5df41938fb 100644 --- a/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json @@ -172,27 +172,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, @@ -2028,4 +2024,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/es_archives/security_solution/migrations/mappings.json b/x-pack/test/functional/es_archives/security_solution/migrations/mappings.json index 5a602322b3017..fa49916066a1a 100644 --- a/x-pack/test/functional/es_archives/security_solution/migrations/mappings.json +++ b/x-pack/test/functional/es_archives/security_solution/migrations/mappings.json @@ -272,27 +272,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0/mappings.json b/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0/mappings.json index 7561dbb8dc6d2..2e6a9bcee3d8c 100644 --- a/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0/mappings.json +++ b/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0/mappings.json @@ -276,27 +276,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, diff --git a/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0_space/mappings.json b/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0_space/mappings.json index 0fa5a458f6995..682b241c126f4 100644 --- a/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0_space/mappings.json +++ b/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0_space/mappings.json @@ -273,27 +273,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, @@ -3089,4 +3085,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/es_archives/visualize/default/mappings.json b/x-pack/test/functional/es_archives/visualize/default/mappings.json index 00dddbcafdd1c..2d761064d0a46 100644 --- a/x-pack/test/functional/es_archives/visualize/default/mappings.json +++ b/x-pack/test/functional/es_archives/visualize/default/mappings.json @@ -254,27 +254,23 @@ }, "apm-indices": { "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } + "errors": { + "type": "keyword" + }, + "metrics": { + "type": "keyword" + }, + "onboarding": { + "type": "keyword" + }, + "sourcemaps": { + "type": "keyword" + }, + "spans": { + "type": "keyword" + }, + "transactions": { + "type": "keyword" } } }, From 1d71d42a7b14e1560bd4fb2ebcaa7d913c0054b7 Mon Sep 17 00:00:00 2001 From: Ashokaditya Date: Wed, 13 Oct 2021 09:25:20 +0200 Subject: [PATCH 003/117] [Security Solution][Endpoint] Host Isolation API changes (#113621) * Use the new data stream (if exists) to write action request to and then the fleet index. Else do as usual. fixes elastic/security-team/issues/1704 * fix legacy tests * add relevant additional tests * remove duplicate test * update tests * cleanup review changes refs elastic/security-team/issues/1704 * fix lint * Use correct mapping keys when writing to index * write record on new index when action request fails to write to `.fleet-actions` review comments * better error message review comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/endpoint/constants.ts | 8 +- .../endpoint_action_generator.ts | 54 +---- .../data_loaders/index_endpoint_actions.ts | 20 +- .../common/endpoint/types/actions.ts | 44 ++++ .../endpoint/routes/actions/isolation.test.ts | 110 ++++++++-- .../endpoint/routes/actions/isolation.ts | 191 +++++++++++++++--- 6 files changed, 326 insertions(+), 101 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/constants.ts b/x-pack/plugins/security_solution/common/endpoint/constants.ts index c7949299c68db..6e9123da2dd9b 100644 --- a/x-pack/plugins/security_solution/common/endpoint/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/constants.ts @@ -5,8 +5,12 @@ * 2.0. */ -export const ENDPOINT_ACTIONS_INDEX = '.logs-endpoint.actions-default'; -export const ENDPOINT_ACTION_RESPONSES_INDEX = '.logs-endpoint.action.responses-default'; +/** endpoint data streams that are used for host isolation */ +/** for index patterns `.logs-endpoint.actions-* and .logs-endpoint.action.responses-*`*/ +export const ENDPOINT_ACTIONS_DS = '.logs-endpoint.actions'; +export const ENDPOINT_ACTIONS_INDEX = `${ENDPOINT_ACTIONS_DS}-default`; +export const ENDPOINT_ACTION_RESPONSES_DS = '.logs-endpoint.action.responses'; +export const ENDPOINT_ACTION_RESPONSES_INDEX = `${ENDPOINT_ACTIONS_DS}-default`; export const eventsIndexPattern = 'logs-endpoint.events.*'; export const alertsIndexPattern = 'logs-endpoint.alerts-*'; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts index 0a39e4ea351f0..dd4eeeab15cce 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts @@ -8,51 +8,7 @@ import { DeepPartial } from 'utility-types'; import { merge } from 'lodash'; import { BaseDataGenerator } from './base_data_generator'; -import { EndpointActionData, ISOLATION_ACTIONS } from '../types'; - -interface EcsError { - code: string; - id: string; - message: string; - stack_trace: string; - type: string; -} - -interface EndpointActionFields { - action_id: string; - data: EndpointActionData; -} - -interface ActionRequestFields { - expiration: string; - type: 'INPUT_ACTION'; - input_type: 'endpoint'; -} - -interface ActionResponseFields { - completed_at: string; - started_at: string; -} -export interface LogsEndpointAction { - '@timestamp': string; - agent: { - id: string | string[]; - }; - EndpointAction: EndpointActionFields & ActionRequestFields; - error?: EcsError; - user: { - id: string; - }; -} - -export interface LogsEndpointActionResponse { - '@timestamp': string; - agent: { - id: string | string[]; - }; - EndpointAction: EndpointActionFields & ActionResponseFields; - error?: EcsError; -} +import { ISOLATION_ACTIONS, LogsEndpointAction, LogsEndpointActionResponse } from '../types'; const ISOLATION_COMMANDS: ISOLATION_ACTIONS[] = ['isolate', 'unisolate']; @@ -66,7 +22,7 @@ export class EndpointActionGenerator extends BaseDataGenerator { agent: { id: [this.randomUUID()], }, - EndpointAction: { + EndpointActions: { action_id: this.randomUUID(), expiration: this.randomFutureDate(timeStamp), type: 'INPUT_ACTION', @@ -86,11 +42,11 @@ export class EndpointActionGenerator extends BaseDataGenerator { } generateIsolateAction(overrides: DeepPartial = {}): LogsEndpointAction { - return merge(this.generate({ EndpointAction: { data: { command: 'isolate' } } }), overrides); + return merge(this.generate({ EndpointActions: { data: { command: 'isolate' } } }), overrides); } generateUnIsolateAction(overrides: DeepPartial = {}): LogsEndpointAction { - return merge(this.generate({ EndpointAction: { data: { command: 'unisolate' } } }), overrides); + return merge(this.generate({ EndpointActions: { data: { command: 'unisolate' } } }), overrides); } /** Generates an endpoint action response */ @@ -105,7 +61,7 @@ export class EndpointActionGenerator extends BaseDataGenerator { agent: { id: this.randomUUID(), }, - EndpointAction: { + EndpointActions: { action_id: this.randomUUID(), completed_at: timeStamp.toISOString(), data: { diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts index bf46214b20f31..e4379271315dd 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts @@ -7,12 +7,8 @@ import { Client } from '@elastic/elasticsearch'; import { DeleteByQueryResponse } from '@elastic/elasticsearch/api/types'; -import { HostMetadata } from '../types'; -import { - EndpointActionGenerator, - LogsEndpointAction, - LogsEndpointActionResponse, -} from '../data_generators/endpoint_action_generator'; +import { HostMetadata, LogsEndpointAction, LogsEndpointActionResponse } from '../types'; +import { EndpointActionGenerator } from '../data_generators/endpoint_action_generator'; import { wrapErrorAndRejectPromise } from './utils'; import { ENDPOINT_ACTIONS_INDEX, ENDPOINT_ACTION_RESPONSES_INDEX } from '../constants'; @@ -49,7 +45,7 @@ export const indexEndpointActionsForHost = async ( for (let i = 0; i < total; i++) { // create an action const action = endpointActionGenerator.generate({ - EndpointAction: { + EndpointActions: { data: { comment: 'data generator: this host is same as bad' }, }, }); @@ -66,9 +62,9 @@ export const indexEndpointActionsForHost = async ( // Create an action response for the above const actionResponse = endpointActionGenerator.generateResponse({ agent: { id: agentId }, - EndpointAction: { - action_id: action.EndpointAction.action_id, - data: action.EndpointAction.data, + EndpointActions: { + action_id: action.EndpointActions.action_id, + data: action.EndpointActions.data, }, }); @@ -174,7 +170,7 @@ export const deleteIndexedEndpointActions = async ( { terms: { action_id: indexedData.endpointActions.map( - (action) => action.EndpointAction.action_id + (action) => action.EndpointActions.action_id ), }, }, @@ -200,7 +196,7 @@ export const deleteIndexedEndpointActions = async ( { terms: { action_id: indexedData.endpointActionResponses.map( - (action) => action.EndpointAction.action_id + (action) => action.EndpointActions.action_id ), }, }, diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index c6d30825c21c9..bc46ca2f5b451 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -10,6 +10,50 @@ import { ActionStatusRequestSchema, HostIsolationRequestSchema } from '../schema export type ISOLATION_ACTIONS = 'isolate' | 'unisolate'; +interface EcsError { + code?: string; + id?: string; + message: string; + stack_trace?: string; + type?: string; +} + +interface EndpointActionFields { + action_id: string; + data: EndpointActionData; +} + +interface ActionRequestFields { + expiration: string; + type: 'INPUT_ACTION'; + input_type: 'endpoint'; +} + +interface ActionResponseFields { + completed_at: string; + started_at: string; +} +export interface LogsEndpointAction { + '@timestamp': string; + agent: { + id: string | string[]; + }; + EndpointActions: EndpointActionFields & ActionRequestFields; + error?: EcsError; + user: { + id: string; + }; +} + +export interface LogsEndpointActionResponse { + '@timestamp': string; + agent: { + id: string | string[]; + }; + EndpointActions: EndpointActionFields & ActionResponseFields; + error?: EcsError; +} + export interface EndpointActionData { command: ISOLATION_ACTIONS; comment?: string; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts index 71df9902223da..ee3bc5e1f21e3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts @@ -34,16 +34,18 @@ import { ISOLATE_HOST_ROUTE, UNISOLATE_HOST_ROUTE, metadataTransformPrefix, + ENDPOINT_ACTIONS_INDEX, } from '../../../../common/endpoint/constants'; import { EndpointAction, HostIsolationRequestBody, HostIsolationResponse, HostMetadata, + LogsEndpointAction, } from '../../../../common/endpoint/types'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; import { legacyMetadataSearchResponse } from '../metadata/support/test_support'; -import { ElasticsearchAssetType } from '../../../../../fleet/common'; +import { AGENT_ACTIONS_INDEX, ElasticsearchAssetType } from '../../../../../fleet/common'; import { CasesClientMock } from '../../../../../cases/server/client/mocks'; interface CallRouteInterface { @@ -109,7 +111,8 @@ describe('Host Isolation', () => { let callRoute: ( routePrefix: string, - opts: CallRouteInterface + opts: CallRouteInterface, + indexExists?: { endpointDsExists: boolean } ) => Promise>; const superUser = { username: 'superuser', @@ -175,22 +178,42 @@ describe('Host Isolation', () => { // it returns the requestContext mock used in the call, to assert internal calls (e.g. the indexed document) callRoute = async ( routePrefix: string, - { body, idxResponse, searchResponse, mockUser, license }: CallRouteInterface + { body, idxResponse, searchResponse, mockUser, license }: CallRouteInterface, + indexExists?: { endpointDsExists: boolean } ): Promise> => { const asUser = mockUser ? mockUser : superUser; (startContract.security.authc.getCurrentUser as jest.Mock).mockImplementationOnce( () => asUser ); + const ctx = createRouteHandlerContext(mockScopedClient, mockSavedObjectClient); - const withIdxResp = idxResponse ? idxResponse : { statusCode: 201 }; - ctx.core.elasticsearch.client.asCurrentUser.index = jest + // mock _index_template + ctx.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = jest .fn() - .mockImplementationOnce(() => Promise.resolve(withIdxResp)); - ctx.core.elasticsearch.client.asCurrentUser.search = jest + .mockImplementationOnce(() => { + if (indexExists) { + return Promise.resolve({ + body: true, + statusCode: 200, + }); + } + return Promise.resolve({ + body: false, + statusCode: 404, + }); + }); + const withIdxResp = idxResponse ? idxResponse : { statusCode: 201 }; + const mockIndexResponse = jest.fn().mockImplementation(() => Promise.resolve(withIdxResp)); + const mockSearchResponse = jest .fn() .mockImplementation(() => Promise.resolve({ body: legacyMetadataSearchResponse(searchResponse) }) ); + if (indexExists) { + ctx.core.elasticsearch.client.asInternalUser.index = mockIndexResponse; + } + ctx.core.elasticsearch.client.asCurrentUser.index = mockIndexResponse; + ctx.core.elasticsearch.client.asCurrentUser.search = mockSearchResponse; const withLicense = license ? license : Platinum; licenseEmitter.next(withLicense); const mockRequest = httpServerMock.createKibanaRequest({ body }); @@ -288,11 +311,6 @@ describe('Host Isolation', () => { ).mock.calls[0][0].body; expect(actionDoc.timeout).toEqual(300); }); - - it('succeeds when just an endpoint ID is provided', async () => { - await callRoute(ISOLATE_HOST_ROUTE, { body: { endpoint_ids: ['XYZ'] } }); - expect(mockResponse.ok).toBeCalled(); - }); it('sends the action to the correct agent when endpoint ID is given', async () => { const doc = docGen.generateHostMetadata(); const AgentID = doc.elastic.agent.id; @@ -326,6 +344,74 @@ describe('Host Isolation', () => { expect(actionDoc.data.command).toEqual('unisolate'); }); + describe('With endpoint data streams', () => { + it('handles unisolation', async () => { + const ctx = await callRoute( + UNISOLATE_HOST_ROUTE, + { + body: { endpoint_ids: ['XYZ'] }, + }, + { endpointDsExists: true } + ); + const actionDocs: [ + { index: string; body: LogsEndpointAction }, + { index: string; body: EndpointAction } + ] = [ + (ctx.core.elasticsearch.client.asCurrentUser.index as jest.Mock).mock.calls[0][0], + (ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock).mock.calls[1][0], + ]; + + expect(actionDocs[0].index).toEqual(ENDPOINT_ACTIONS_INDEX); + expect(actionDocs[1].index).toEqual(AGENT_ACTIONS_INDEX); + expect(actionDocs[0].body.EndpointActions.data.command).toEqual('unisolate'); + expect(actionDocs[1].body.data.command).toEqual('unisolate'); + }); + + it('handles isolation', async () => { + const ctx = await callRoute( + ISOLATE_HOST_ROUTE, + { + body: { endpoint_ids: ['XYZ'] }, + }, + { endpointDsExists: true } + ); + const actionDocs: [ + { index: string; body: LogsEndpointAction }, + { index: string; body: EndpointAction } + ] = [ + (ctx.core.elasticsearch.client.asCurrentUser.index as jest.Mock).mock.calls[0][0], + (ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock).mock.calls[1][0], + ]; + + expect(actionDocs[0].index).toEqual(ENDPOINT_ACTIONS_INDEX); + expect(actionDocs[1].index).toEqual(AGENT_ACTIONS_INDEX); + expect(actionDocs[0].body.EndpointActions.data.command).toEqual('isolate'); + expect(actionDocs[1].body.data.command).toEqual('isolate'); + }); + + it('handles errors', async () => { + const ErrMessage = 'Uh oh!'; + await callRoute( + UNISOLATE_HOST_ROUTE, + { + body: { endpoint_ids: ['XYZ'] }, + idxResponse: { + statusCode: 500, + body: { + result: ErrMessage, + }, + }, + }, + { endpointDsExists: true } + ); + + expect(mockResponse.ok).not.toBeCalled(); + const response = mockResponse.customError.mock.calls[0][0]; + expect(response.statusCode).toEqual(500); + expect((response.body as Error).message).toEqual(ErrMessage); + }); + }); + describe('License Level', () => { it('allows platinum license levels to isolate hosts', async () => { await callRoute(ISOLATE_HOST_ROUTE, { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts index 45f0e851dfdd1..4652630649ffc 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts @@ -6,15 +6,25 @@ */ import moment from 'moment'; -import { RequestHandler } from 'src/core/server'; +import { RequestHandler, Logger } from 'src/core/server'; import uuid from 'uuid'; import { TypeOf } from '@kbn/config-schema'; import { CommentType } from '../../../../../cases/common'; import { CasesByAlertId } from '../../../../../cases/common/api/cases/case'; import { HostIsolationRequestSchema } from '../../../../common/endpoint/schema/actions'; -import { ISOLATE_HOST_ROUTE, UNISOLATE_HOST_ROUTE } from '../../../../common/endpoint/constants'; +import { + ENDPOINT_ACTIONS_DS, + ENDPOINT_ACTION_RESPONSES_DS, + ISOLATE_HOST_ROUTE, + UNISOLATE_HOST_ROUTE, +} from '../../../../common/endpoint/constants'; import { AGENT_ACTIONS_INDEX } from '../../../../../fleet/common'; -import { EndpointAction, HostMetadata } from '../../../../common/endpoint/types'; +import { + EndpointAction, + HostMetadata, + LogsEndpointAction, + LogsEndpointActionResponse, +} from '../../../../common/endpoint/types'; import { SecuritySolutionPluginRouter, SecuritySolutionRequestHandlerContext, @@ -52,6 +62,57 @@ export function registerHostIsolationRoutes( ); } +const createFailedActionResponseEntry = async ({ + context, + doc, + logger, +}: { + context: SecuritySolutionRequestHandlerContext; + doc: LogsEndpointActionResponse; + logger: Logger; +}): Promise => { + const esClient = context.core.elasticsearch.client.asCurrentUser; + try { + await esClient.index({ + index: `${ENDPOINT_ACTION_RESPONSES_DS}-default`, + body: { + ...doc, + error: { + code: '424', + message: 'Failed to deliver action request to fleet', + }, + }, + }); + } catch (e) { + logger.error(e); + } +}; + +const doLogsEndpointActionDsExists = async ({ + context, + logger, + dataStreamName, +}: { + context: SecuritySolutionRequestHandlerContext; + logger: Logger; + dataStreamName: string; +}): Promise => { + try { + const esClient = context.core.elasticsearch.client.asInternalUser; + const doesIndexTemplateExist = await esClient.indices.existsIndexTemplate({ + name: dataStreamName, + }); + return doesIndexTemplateExist.statusCode === 404 ? false : true; + } catch (error) { + const errorType = error?.type ?? ''; + if (errorType !== 'resource_not_found_exception') { + logger.error(error); + throw error; + } + return false; + } +}; + export const isolationRequestHandler = function ( endpointContext: EndpointAppContext, isolate: boolean @@ -106,43 +167,121 @@ export const isolationRequestHandler = function ( caseIDs = [...new Set(caseIDs)]; // create an Action ID and dispatch it to ES & Fleet Server - const esClient = context.core.elasticsearch.client.asCurrentUser; const actionID = uuid.v4(); - let result; + + let fleetActionIndexResult; + let logsEndpointActionsResult; + + const agents = endpointData.map((endpoint: HostMetadata) => endpoint.elastic.agent.id); + const doc = { + '@timestamp': moment().toISOString(), + agent: { + id: agents, + }, + EndpointActions: { + action_id: actionID, + expiration: moment().add(2, 'weeks').toISOString(), + type: 'INPUT_ACTION', + input_type: 'endpoint', + data: { + command: isolate ? 'isolate' : 'unisolate', + comment: req.body.comment ?? undefined, + }, + } as Omit, + user: { + id: user!.username, + }, + }; + + // if .logs-endpoint.actions data stream exists + // try to create action request record in .logs-endpoint.actions DS as the current user + // (from >= v7.16, use this check to ensure the current user has privileges to write to the new index) + // and allow only users with superuser privileges to write to fleet indices + const logger = endpointContext.logFactory.get('host-isolation'); + const doesLogsEndpointActionsDsExist = await doLogsEndpointActionDsExists({ + context, + logger, + dataStreamName: ENDPOINT_ACTIONS_DS, + }); + // if the new endpoint indices/data streams exists + // write the action request to the new index as the current user + if (doesLogsEndpointActionsDsExist) { + try { + const esClient = context.core.elasticsearch.client.asCurrentUser; + logsEndpointActionsResult = await esClient.index({ + index: `${ENDPOINT_ACTIONS_DS}-default`, + body: { + ...doc, + }, + }); + if (logsEndpointActionsResult.statusCode !== 201) { + return res.customError({ + statusCode: 500, + body: { + message: logsEndpointActionsResult.body.result, + }, + }); + } + } catch (e) { + return res.customError({ + statusCode: 500, + body: { message: e }, + }); + } + } + try { - result = await esClient.index({ + let esClient = context.core.elasticsearch.client.asCurrentUser; + if (doesLogsEndpointActionsDsExist) { + // create action request record as system user with user in .fleet-actions + esClient = context.core.elasticsearch.client.asInternalUser; + } + // write as the current user if the new indices do not exist + // ({ index: AGENT_ACTIONS_INDEX, body: { - action_id: actionID, - '@timestamp': moment().toISOString(), - expiration: moment().add(2, 'weeks').toISOString(), - type: 'INPUT_ACTION', - input_type: 'endpoint', - agents: endpointData.map((endpt: HostMetadata) => endpt.elastic.agent.id), - user_id: user!.username, + ...doc.EndpointActions, + '@timestamp': doc['@timestamp'], + agents, timeout: 300, // 5 minutes - data: { - command: isolate ? 'isolate' : 'unisolate', - comment: req.body.comment ?? undefined, - }, + user_id: doc.user.id, }, }); + + if (fleetActionIndexResult.statusCode !== 201) { + return res.customError({ + statusCode: 500, + body: { + message: fleetActionIndexResult.body.result, + }, + }); + } } catch (e) { + // create entry in .logs-endpoint.action.responses-default data stream + // when writing to .fleet-actions fails + if (doesLogsEndpointActionsDsExist) { + await createFailedActionResponseEntry({ + context, + doc: { + '@timestamp': moment().toISOString(), + agent: doc.agent, + EndpointActions: { + action_id: doc.EndpointActions.action_id, + completed_at: moment().toISOString(), + started_at: moment().toISOString(), + data: doc.EndpointActions.data, + }, + }, + logger, + }); + } return res.customError({ statusCode: 500, body: { message: e }, }); } - if (result.statusCode !== 201) { - return res.customError({ - statusCode: 500, - body: { - message: result.body.result, - }, - }); - } - // Update all cases with a comment if (caseIDs.length > 0) { const targets = endpointData.map((endpt: HostMetadata) => ({ From aad477185d689c9e04e25326b3b8181f26051c88 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 13 Oct 2021 10:08:08 +0200 Subject: [PATCH 004/117] [Lens] Keep the custom label when transitioning to/from Formula (#114270) * :bug: Keep the custom label when transitioning to/from Formula * :bug: Fix transition bug with padding Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dimension_panel/dimension_editor.tsx | 6 +- .../operations/layer_helpers.test.ts | 116 ++++++++++++++++++ .../operations/layer_helpers.ts | 33 +++-- 3 files changed, 145 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 2f1c00bc5cca0..8286ab492f14d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -731,9 +731,9 @@ export function DimensionEditor(props: DimensionEditorProps) { /> {TabContent} - {!isFullscreen && !currentFieldIsInvalid && temporaryState === 'none' && ( + {!isFullscreen && !currentFieldIsInvalid && (
- {!incompleteInfo && selectedColumn && ( + {!incompleteInfo && selectedColumn && temporaryState === 'none' && ( { @@ -756,7 +756,7 @@ export function DimensionEditor(props: DimensionEditorProps) { /> )} - {!isFullscreen && !incompleteInfo && !hideGrouping && ( + {!isFullscreen && !incompleteInfo && !hideGrouping && temporaryState === 'none' && ( { }).columns.col1 ).toEqual(expect.objectContaining({ label: 'Average of bytes' })); }); + + it('should carry over a custom label when transitioning to a managed reference', () => { + expect( + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'MY CUSTOM LABEL', + customLabel: true, + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'source', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 5, + }, + }, + }, + }, + indexPattern, + columnId: 'col1', + op: 'formula', + field: indexPattern.fields[2], // bytes field + visualizationGroups: [], + shouldResetLabel: undefined, + }).columns.col1 + ).toEqual(expect.objectContaining({ label: 'MY CUSTOM LABEL' })); + }); + + it('should overwrite the current label when transitioning to a managed reference operation when not custom', () => { + expect( + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Average of bytes', + dataType: 'number', + isBucketed: false, + operationType: 'average', + sourceField: 'bytes', + }, + }, + }, + indexPattern, + columnId: 'col1', + op: 'formula', + field: indexPattern.fields[2], // bytes field + visualizationGroups: [], + shouldResetLabel: undefined, + }).columns.col1 + ).toEqual(expect.objectContaining({ label: 'average(bytes)' })); + }); + + it('should carry over a custom label when transitioning from a managed reference', () => { + expect( + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'MY CUSTOM LABEL', + customLabel: true, + dataType: 'number', + operationType: 'formula', + isBucketed: false, + scale: 'ratio', + params: { isFormulaBroken: false, formula: 'average(bytes)' }, + references: [], + }, + }, + }, + indexPattern, + columnId: 'col1', + op: 'average', + field: indexPattern.fields[2], // bytes field + visualizationGroups: [], + shouldResetLabel: undefined, + }).columns.col1 + ).toEqual(expect.objectContaining({ label: 'MY CUSTOM LABEL' })); + }); + + it('should not carry over the managed reference default label to the new operation', () => { + expect( + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'average(bytes)', + customLabel: true, + dataType: 'number', + operationType: 'formula', + isBucketed: false, + scale: 'ratio', + params: { isFormulaBroken: false, formula: 'average(bytes)' }, + references: [], + }, + }, + }, + indexPattern, + columnId: 'col1', + op: 'average', + field: indexPattern.fields[2], // bytes field + visualizationGroups: [], + shouldResetLabel: undefined, + }).columns.col1 + ).toEqual(expect.objectContaining({ label: 'Average of bytes' })); + }); }); it('should execute adjustments for other columns', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index baacc7bb64d16..b3b98e5054aa6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -417,6 +417,17 @@ export function replaceColumn({ field, visualizationGroups, }); + + // if the formula label is not the default one, propagate it to the new operation + if ( + !shouldResetLabel && + previousColumn.customLabel && + previousColumn.label !== + previousDefinition.getDefaultLabel(previousColumn, indexPattern, tempLayer.columns) + ) { + hypotheticalLayer.columns[columnId].customLabel = true; + hypotheticalLayer.columns[columnId].label = previousColumn.label; + } if (hypotheticalLayer.incompleteColumns && hypotheticalLayer.incompleteColumns[columnId]) { return { ...layer, @@ -500,13 +511,10 @@ export function replaceColumn({ // TODO: Refactor all this to be more generic and know less about Formula // if managed it has to look at the full picture to have a seamless transition if (operationDefinition.input === 'managedReference') { - const newColumn = copyCustomLabel( - operationDefinition.buildColumn( - { ...baseOptions, layer: tempLayer }, - previousColumn.params, - operationDefinitionMap - ), - previousColumn + const newColumn = operationDefinition.buildColumn( + { ...baseOptions, layer: tempLayer }, + previousColumn.params, + operationDefinitionMap ) as FormulaIndexPatternColumn; // now remove the previous references @@ -535,6 +543,17 @@ export function replaceColumn({ newLayer = basicLayer; } + // when coming to Formula keep the custom label + const regeneratedColumn = newLayer.columns[columnId]; + if ( + !shouldResetLabel && + regeneratedColumn.operationType !== previousColumn.operationType && + previousColumn.customLabel + ) { + regeneratedColumn.customLabel = true; + regeneratedColumn.label = previousColumn.label; + } + return updateDefaultLabels( { ...tempLayer, From 62d39cc1fdbc8a4240e3ba9d8ac125923a673e52 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 13 Oct 2021 09:10:15 +0100 Subject: [PATCH 005/117] [Fleet] Add Integration Policy Page Improvements (#114556) * Show add agent link instead of 0 agents * Add popover * open and close popover * fill button * add popover to agent cell * PR feedback * only add offset to props if it's provided * make code clearer * Update x-pack/plugins/fleet/public/components/add_agent_help_popover.tsx Co-authored-by: Dave Snider * remove unused import * whitespace Co-authored-by: Dave Snider Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../agent_policy/details_page/index.tsx | 112 +++++++++++++----- .../package_policy_agents_cell.test.tsx | 29 ++++- .../components/package_policy_agents_cell.tsx | 40 +++++-- .../detail/policies/package_policies.tsx | 7 +- .../components/add_agent_help_popover.tsx | 88 ++++++++++++++ .../plugins/fleet/public/components/index.ts | 1 + 6 files changed, 234 insertions(+), 43 deletions(-) create mode 100644 x-pack/plugins/fleet/public/components/add_agent_help_popover.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx index eec7d80b75c4b..76994feb18fea 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx @@ -10,17 +10,19 @@ import { Redirect, useRouteMatch, Switch, Route, useHistory, useLocation } from import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; import { + EuiButtonEmpty, + EuiDescriptionList, + EuiDescriptionListDescription, + EuiDescriptionListTitle, EuiFlexGroup, EuiFlexItem, + EuiI18nNumber, EuiIconTip, - EuiTitle, - EuiText, + EuiLink, + EuiPortal, EuiSpacer, - EuiButtonEmpty, - EuiI18nNumber, - EuiDescriptionList, - EuiDescriptionListTitle, - EuiDescriptionListDescription, + EuiText, + EuiTitle, } from '@elastic/eui'; import type { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; import styled from 'styled-components'; @@ -36,7 +38,7 @@ import { useFleetStatus, useIntraAppState, } from '../../../hooks'; -import { Loading, Error } from '../../../components'; +import { Loading, Error, AgentEnrollmentFlyout, AddAgentHelpPopover } from '../../../components'; import { WithHeaderLayout } from '../../../layouts'; import { LinkedAgentCount, AgentPolicyActionMenu } from '../components'; @@ -58,7 +60,16 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { const agentPolicyRequest = useGetOneAgentPolicy(policyId); const agentPolicy = agentPolicyRequest.data ? agentPolicyRequest.data.item : null; const { isLoading, error, sendRequest: refreshAgentPolicy } = agentPolicyRequest; + const queryParams = new URLSearchParams(useLocation().search); + const openEnrollmentFlyoutOpenByDefault = queryParams.get('openEnrollmentFlyout') === 'true'; + const openAddAgentHelpPopoverOpenByDefault = queryParams.get('showAddAgentHelp') === 'true'; const [redirectToAgentPolicyList] = useState(false); + const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState( + openEnrollmentFlyoutOpenByDefault + ); + const [isAddAgentHelpPopoverOpen, setIsAddAgentHelpPopoverOpen] = useState( + openAddAgentHelpPopoverOpenByDefault + ); const agentStatusRequest = useGetAgentStatus(policyId); const { refreshAgentStatus } = agentStatusRequest; const { @@ -66,8 +77,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { } = useStartServices(); const routeState = useIntraAppState(); const agentStatus = agentStatusRequest.data?.results; - const queryParams = new URLSearchParams(useLocation().search); - const openEnrollmentFlyoutOpenByDefault = queryParams.get('openEnrollmentFlyout') === 'true'; + const { isReady: isFleetReady } = useFleetStatus(); const headerLeftContent = useMemo( @@ -138,6 +148,25 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { [getHref, isLoading, agentPolicy, policyId] ); + const onCancelEnrollment = useMemo(() => { + if (routeState && routeState.onDoneNavigateTo && isFleetReady) { + const [appId, options] = routeState.onDoneNavigateTo; + return () => navigateToApp(appId, options); + } + + return undefined; + }, [isFleetReady, navigateToApp, routeState]); + + const addAgentLink = ( + { + setIsAddAgentHelpPopoverOpen(false); + setIsEnrollmentFlyoutOpen(true); + }} + > + + + ); const headerRightContent = useMemo( () => agentPolicy ? ( @@ -168,15 +197,25 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { { isDivider: true }, { label: i18n.translate('xpack.fleet.policyDetails.summary.usedBy', { - defaultMessage: 'Used by', + defaultMessage: 'Agents', }), - content: ( - - ), + content: + agentStatus && agentStatus!.total ? ( + + ) : ( + { + setIsAddAgentHelpPopoverOpen(false); + }} + /> + ), }, { isDivider: true }, { @@ -203,16 +242,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { onCopySuccess={(newAgentPolicy: AgentPolicy) => { history.push(getPath('policy_details', { policyId: newAgentPolicy.id })); }} - enrollmentFlyoutOpenByDefault={openEnrollmentFlyoutOpenByDefault} - onCancelEnrollment={ - routeState && routeState.onDoneNavigateTo && isFleetReady - ? () => - navigateToApp( - routeState.onDoneNavigateTo![0], - routeState.onDoneNavigateTo![1] - ) - : undefined - } + onCancelEnrollment={onCancelEnrollment} /> ), }, @@ -237,7 +267,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { ) : undefined, /* eslint-disable-next-line react-hooks/exhaustive-deps */ - [agentPolicy, policyId, agentStatus] + [agentPolicy, policyId, agentStatus, isAddAgentHelpPopoverOpen] ); const headerTabs = useMemo(() => { @@ -303,8 +333,28 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { ); } - return ; - }, [agentPolicy, policyId, error, isLoading, redirectToAgentPolicyList]); + return ( + <> + {isEnrollmentFlyoutOpen && ( + + setIsEnrollmentFlyoutOpen(false))} + /> + + )} + ; + + ); + }, [ + redirectToAgentPolicyList, + isLoading, + error, + agentPolicy, + isEnrollmentFlyoutOpen, + onCancelEnrollment, + policyId, + ]); return ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx index 8872c61299093..6cb9aab005e77 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx @@ -13,7 +13,12 @@ import { createIntegrationsTestRendererMock } from '../../../../../../../../mock import { PackagePolicyAgentsCell } from './package_policy_agents_cell'; -function renderCell({ agentCount = 0, agentPolicyId = '123', onAddAgent = () => {} }) { +function renderCell({ + agentCount = 0, + agentPolicyId = '123', + onAddAgent = () => {}, + hasHelpPopover = false, +}) { const renderer = createIntegrationsTestRendererMock(); return renderer.render( @@ -21,6 +26,7 @@ function renderCell({ agentCount = 0, agentPolicyId = '123', onAddAgent = () => agentCount={agentCount} agentPolicyId={agentPolicyId} onAddAgent={onAddAgent} + hasHelpPopover={hasHelpPopover} /> ); } @@ -40,4 +46,25 @@ describe('PackagePolicyAgentsCell', () => { expect(utils.queryByText('9999')).toBeInTheDocument(); }); }); + + test('it should display help popover if count is 0 and hasHelpPopover=true', async () => { + const utils = renderCell({ agentCount: 0, hasHelpPopover: true }); + await act(async () => { + expect(utils.queryByText('9999')).not.toBeInTheDocument(); + expect(utils.queryByText('Add agent')).toBeInTheDocument(); + expect( + utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') + ).toBeInTheDocument(); + }); + }); + test('it should not display help popover if count is > 0 and hasHelpPopover=true', async () => { + const utils = renderCell({ agentCount: 9999, hasHelpPopover: true }); + await act(async () => { + expect(utils.queryByText('9999')).toBeInTheDocument(); + expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); + expect( + utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') + ).not.toBeInTheDocument(); + }); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx index 37543e7e5ae1b..e70d10e735571 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx @@ -5,20 +5,43 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { LinkedAgentCount } from '../../../../../../components'; +import { LinkedAgentCount, AddAgentHelpPopover } from '../../../../../../components'; + +const AddAgentButton = ({ onAddAgent }: { onAddAgent: () => void }) => ( + + + +); + +const AddAgentButtonWithPopover = ({ onAddAgent }: { onAddAgent: () => void }) => { + const button = ; + const [isHelpOpen, setIsHelpOpen] = useState(true); + return ( + setIsHelpOpen(false)} + /> + ); +}; export const PackagePolicyAgentsCell = ({ agentPolicyId, agentCount = 0, onAddAgent, + hasHelpPopover = false, }: { agentPolicyId: string; agentCount?: number; + hasHelpPopover?: boolean; onAddAgent: () => void; }) => { if (agentCount > 0) { @@ -31,12 +54,9 @@ export const PackagePolicyAgentsCell = ({ ); } - return ( - - - - ); + if (!hasHelpPopover) { + return ; + } + + return ; }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index 304bdd621b1b2..2a7cab929211a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -81,6 +81,10 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps () => queryParams.get('addAgentToPolicyId'), [queryParams] ); + const showAddAgentHelpForPolicyId = useMemo( + () => queryParams.get('showAddAgentHelpForPolicyId'), + [queryParams] + ); const [flyoutOpenForPolicyId, setFlyoutOpenForPolicyId] = useState( agentPolicyIdFromParams ); @@ -294,6 +298,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps agentPolicyId={agentPolicy.id} agentCount={agentPolicy.agents} onAddAgent={() => setFlyoutOpenForPolicyId(agentPolicy.id)} + hasHelpPopover={showAddAgentHelpForPolicyId === agentPolicy.id} /> ); }, @@ -321,7 +326,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps }, }, ], - [getHref, viewDataStep] + [getHref, showAddAgentHelpForPolicyId, viewDataStep] ); const noItemsMessage = useMemo(() => { diff --git a/x-pack/plugins/fleet/public/components/add_agent_help_popover.tsx b/x-pack/plugins/fleet/public/components/add_agent_help_popover.tsx new file mode 100644 index 0000000000000..e47070fdc7b99 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/add_agent_help_popover.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { ReactElement } from 'react'; +import React from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +import type { NoArgCallback } from '@elastic/eui'; +import { EuiTourStep, EuiLink, EuiText } from '@elastic/eui'; + +import { useStartServices } from '../hooks'; + +export const AddAgentHelpPopover = ({ + button, + isOpen, + offset, + closePopover, +}: { + button: ReactElement; + isOpen: boolean; + offset?: number; + closePopover: NoArgCallback; +}) => { + const { docLinks } = useStartServices(); + + const optionalProps: { offset?: number } = {}; + + if (offset !== undefined) { + optionalProps.offset = offset; // offset being present in props sets it to 0 so only add if specified + } + + return ( + + Elastic Agent, + learnMoreLink: ( + + + + ), + }} + /> + + } + isStepOpen={isOpen} + minWidth={300} + onFinish={() => {}} + step={1} + stepsTotal={1} + title={ + + } + anchorPosition="downCenter" + subtitle={null} + data-test-subj="addAgentHelpPopover" + footerAction={ + { + closePopover(); + }} + > + + + } + > + {button} + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/index.ts b/x-pack/plugins/fleet/public/components/index.ts index dca0a92076180..3252315312d02 100644 --- a/x-pack/plugins/fleet/public/components/index.ts +++ b/x-pack/plugins/fleet/public/components/index.ts @@ -19,6 +19,7 @@ export { AgentPolicyPackageBadges } from './agent_policy_package_badges'; export { DangerEuiContextMenuItem } from './danger_eui_context_menu_item'; export { PackagePolicyDeleteProvider } from './package_policy_delete_provider'; export { PackagePolicyActionsMenu } from './package_policy_actions_menu'; +export { AddAgentHelpPopover } from './add_agent_help_popover'; export * from './link_and_revision'; export * from './settings_flyout'; export * from './agent_enrollment_flyout'; From d9ef453b268098a2f0cb8c6f2e93bfb1cfc1efe3 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 13 Oct 2021 11:31:04 +0300 Subject: [PATCH 006/117] [Discover] Removing SavedObject usage for savedSearch (#112983) * [Discover] Step 2 - remove SavedObjectLoader * Fix PR comments * fix test names * fix ts error * add handling of missed 'so' * add Embeddable error * fix jest * add DiscoverError component * fix Joe comments * add search params * add throwErrorOnUrlConflict util method * add error handling into transform plugin * do some updates * add spaces into visualize, visualizations * fix Tim's comment * pass false into createGetterSetter for getSpaces * Fix comments * Fix lint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Uladzislau Lasitsa --- src/plugins/discover/kibana.json | 4 +- .../discover/public/__mocks__/saved_search.ts | 34 ----- .../discover/public/__mocks__/services.ts | 2 - .../apps/context/services/context.ts | 6 +- .../components/layout/discover_documents.tsx | 4 +- .../components/layout/discover_layout.tsx | 25 ++- .../components/top_nav/on_save_search.tsx | 33 ++-- .../apps/main/discover_main_route.tsx | 122 ++++++++++----- .../apps/main/services/discover_state.test.ts | 2 +- .../main/services/use_discover_state.test.ts | 14 ++ .../apps/main/services/use_discover_state.ts | 9 +- .../apps/main/services/use_saved_search.ts | 4 +- .../application/apps/main/utils/fetch_all.ts | 6 +- .../apps/main/utils/fetch_chart.ts | 6 +- .../apps/main/utils/fetch_documents.ts | 4 +- .../apps/main/utils/fetch_total_hits.ts | 4 +- .../main/utils/get_chart_agg_config.test.ts | 4 +- .../apps/main/utils/get_chart_agg_configs.ts | 4 +- .../apps/main/utils/get_dimensions.test.ts | 4 +- .../apps/main/utils/get_fetch_observable.ts | 4 +- .../apps/main/utils/get_sharing_data.ts | 2 +- .../apps/main/utils/get_state_defaults.ts | 9 +- .../apps/main/utils/persist_saved_search.ts | 9 +- .../apps/main/utils/resolve_index_pattern.ts | 4 +- .../main/utils/update_search_source.test.ts | 2 +- .../apps/main/utils/update_search_source.ts | 2 +- .../helpers/update_search_source.test.ts | 2 +- .../embeddable/saved_search_embeddable.tsx | 5 +- .../embeddable/search_embeddable_factory.ts | 26 +++- .../public/application/embeddable/types.ts | 2 +- .../embeddable/view_saved_search_action.ts | 3 +- src/plugins/discover/public/build_services.ts | 14 +- src/plugins/discover/public/index.ts | 13 +- src/plugins/discover/public/mocks.ts | 4 +- src/plugins/discover/public/plugin.tsx | 18 ++- .../public/saved_searches/constants.ts | 10 ++ .../saved_searches/get_saved_searches.test.ts | 144 ++++++++++++++++++ .../saved_searches/get_saved_searches.ts | 85 +++++++++++ .../discover/public/saved_searches/index.ts | 24 ++- .../{ => legacy}/_saved_search.ts | 11 +- .../public/saved_searches/legacy/index.ts | 10 ++ .../{ => legacy}/saved_searches.ts | 8 +- .../public/saved_searches/legacy/types.ts | 34 +++++ .../save_saved_searches.test.ts | 117 ++++++++++++++ .../saved_searches/save_saved_searches.ts | 80 ++++++++++ .../saved_search_alias_match_redirect.test.ts | 73 +++++++++ .../saved_search_alias_match_redirect.ts | 49 ++++++ ...saved_search_url_conflict_callout.test.tsx | 61 ++++++++ .../saved_search_url_conflict_callout.ts | 46 ++++++ .../saved_searches_utils.test.ts | 140 +++++++++++++++++ .../saved_searches/saved_searches_utils.ts | 57 +++++++ .../discover/public/saved_searches/types.ts | 49 +++--- src/plugins/discover/tsconfig.json | 4 +- src/plugins/vis_default_editor/kibana.json | 2 +- .../public/components/sidebar/sidebar.tsx | 4 +- .../components/sidebar/sidebar_title.tsx | 8 +- src/plugins/visualizations/public/plugin.ts | 15 +- .../public/saved_visualizations/_saved_vis.ts | 4 +- src/plugins/visualizations/public/services.ts | 18 +-- src/plugins/visualizations/public/vis.ts | 22 ++- .../visualize/public/application/types.ts | 19 +-- .../utils/get_visualization_instance.test.ts | 25 +-- .../utils/get_visualization_instance.ts | 21 ++- src/plugins/visualize/public/plugin.ts | 4 +- .../get_csv_panel_action.test.ts | 4 +- .../panel_actions/get_csv_panel_action.tsx | 2 +- x-pack/plugins/transform/kibana.json | 1 + .../public/__mocks__/shared_imports.ts | 2 +- .../transform/public/app/app_dependencies.tsx | 2 + .../app/hooks/use_search_items/common.ts | 7 - .../use_search_items/use_search_items.ts | 18 ++- .../public/app/mount_management_section.ts | 3 +- x-pack/plugins/transform/public/plugin.ts | 2 + .../transform/public/shared_imports.ts | 5 +- 74 files changed, 1318 insertions(+), 282 deletions(-) create mode 100644 src/plugins/discover/public/saved_searches/constants.ts create mode 100644 src/plugins/discover/public/saved_searches/get_saved_searches.test.ts create mode 100644 src/plugins/discover/public/saved_searches/get_saved_searches.ts rename src/plugins/discover/public/saved_searches/{ => legacy}/_saved_search.ts (80%) create mode 100644 src/plugins/discover/public/saved_searches/legacy/index.ts rename src/plugins/discover/public/saved_searches/{ => legacy}/saved_searches.ts (78%) create mode 100644 src/plugins/discover/public/saved_searches/legacy/types.ts create mode 100644 src/plugins/discover/public/saved_searches/save_saved_searches.test.ts create mode 100644 src/plugins/discover/public/saved_searches/save_saved_searches.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.test.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.test.tsx create mode 100644 src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_searches_utils.test.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_searches_utils.ts diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index 46eeb5af1470d..3d5fdefd276d3 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -15,8 +15,8 @@ "savedObjects", "indexPatternFieldEditor" ], - "optionalPlugins": ["home", "share", "usageCollection"], - "requiredBundles": ["kibanaUtils", "home", "kibanaReact", "fieldFormats"], + "optionalPlugins": ["home", "share", "usageCollection", "spaces"], + "requiredBundles": ["kibanaUtils", "home", "kibanaReact", "fieldFormats", "dataViews"], "extraPublicDirs": ["common"], "owner": { "name": "Data Discovery", diff --git a/src/plugins/discover/public/__mocks__/saved_search.ts b/src/plugins/discover/public/__mocks__/saved_search.ts index ebe65a5770356..a488fe7e04c50 100644 --- a/src/plugins/discover/public/__mocks__/saved_search.ts +++ b/src/plugins/discover/public/__mocks__/saved_search.ts @@ -13,44 +13,10 @@ import { indexPatternWithTimefieldMock } from './index_pattern_with_timefield'; export const savedSearchMock = { id: 'the-saved-search-id', - type: 'search', - attributes: { - title: 'the-saved-search-title', - kibanaSavedObjectMeta: { - searchSourceJSON: - '{"highlightAll":true,"version":true,"query":{"query":"foo : \\"bar\\" ","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', - }, - }, - references: [ - { - name: 'kibanaSavedObjectMeta.searchSourceJSON.index', - type: 'index-pattern', - id: 'the-index-pattern-id', - }, - ], - migrationVersion: { search: '7.5.0' }, - error: undefined, searchSource: createSearchSourceMock({ index: indexPatternMock }), } as unknown as SavedSearch; export const savedSearchMockWithTimeField = { id: 'the-saved-search-id-with-timefield', - type: 'search', - attributes: { - title: 'the-saved-search-title', - kibanaSavedObjectMeta: { - searchSourceJSON: - '{"highlightAll":true,"version":true,"query":{"query":"foo : \\"bar\\" ","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', - }, - }, - references: [ - { - name: 'kibanaSavedObjectMeta.searchSourceJSON.index', - type: 'index-pattern', - id: 'the-index-pattern-id', - }, - ], - migrationVersion: { search: '7.5.0' }, - error: undefined, searchSource: createSearchSourceMock({ index: indexPatternWithTimefieldMock }), } as unknown as SavedSearch; diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index 30d66b113e528..8cc5ccf5aa121 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -16,7 +16,6 @@ import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING, } from '../../common'; -import { savedSearchMock } from './saved_search'; import { UI_SETTINGS } from '../../../data/common'; import { TopNavMenu } from '../../../navigation/public'; import { FORMATS_UI_SETTINGS } from 'src/plugins/field_formats/common'; @@ -78,7 +77,6 @@ export const discoverServiceMock = { editIndexPattern: jest.fn(), }, }, - getSavedSearchById: (id?: string) => Promise.resolve(savedSearchMock), navigation: { ui: { TopNavMenu }, }, diff --git a/src/plugins/discover/public/application/apps/context/services/context.ts b/src/plugins/discover/public/application/apps/context/services/context.ts index b76b5ac648c22..257ae2dcce834 100644 --- a/src/plugins/discover/public/application/apps/context/services/context.ts +++ b/src/plugins/discover/public/application/apps/context/services/context.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 { Filter, IndexPattern, SearchSource } from 'src/plugins/data/public'; +import { Filter, IndexPattern, ISearchSource } from 'src/plugins/data/public'; import { reverseSortDir, SortDirection } from './utils/sorting'; import { convertIsoToMillis, extractNanos } from './utils/date_conversion'; import { fetchHitsInInterval } from './utils/fetch_hits_in_interval'; @@ -53,7 +53,7 @@ export async function fetchSurroundingDocs( } const { data } = getServices(); const timeField = indexPattern.timeFieldName!; - const searchSource = data.search.searchSource.createEmpty() as SearchSource; + const searchSource = data.search.searchSource.createEmpty(); updateSearchSource(searchSource, indexPattern, filters, Boolean(useNewFieldsApi)); const sortDirToApply = type === SurrDocType.SUCCESSORS ? sortDir : reverseSortDir(sortDir); @@ -104,7 +104,7 @@ export async function fetchSurroundingDocs( } export function updateSearchSource( - searchSource: SearchSource, + searchSource: ISearchSource, indexPattern: IndexPattern, filters: Filter[], useNewFieldsApi: boolean diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx index e0e0c9c6f8831..d6ede9aa7fe5f 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx @@ -134,7 +134,7 @@ function DiscoverDocumentsComponent({ sort={state.sort || []} isLoading={isLoading} searchDescription={savedSearch.description} - sharedItemTitle={savedSearch.lastSavedTitle} + sharedItemTitle={savedSearch.title} onAddColumn={onAddColumn} onFilter={onAddFilter as DocViewFilterFn} onMoveColumn={onMoveColumn} @@ -156,7 +156,7 @@ function DiscoverDocumentsComponent({ sort={(state.sort as SortPairArr[]) || []} sampleSize={sampleSize} searchDescription={savedSearch.description} - searchTitle={savedSearch.lastSavedTitle} + searchTitle={savedSearch.title} setExpandedDoc={setExpandedDoc} showTimeCol={showTimeCol} services={services} diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx index 4bbef32dcbadd..6c6393daf30a3 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx @@ -39,6 +39,10 @@ import { useDataGridColumns } from '../../../../helpers/use_data_grid_columns'; import { DiscoverDocuments } from './discover_documents'; import { FetchStatus } from '../../../../types'; import { useDataState } from '../../utils/use_data_state'; +import { + SavedSearchURLConflictCallout, + useSavedSearchAliasMatchRedirect, +} from '../../../../../saved_searches'; /** * Local storage key for sidebar persistence state @@ -65,10 +69,18 @@ export function DiscoverLayout({ state, stateContainer, }: DiscoverLayoutProps) { - const { trackUiMetric, capabilities, indexPatterns, data, uiSettings, filterManager, storage } = - services; + const { + trackUiMetric, + capabilities, + indexPatterns, + data, + uiSettings, + filterManager, + storage, + history, + spaces, + } = services; const { main$, charts$, totalHits$ } = savedSearchData$; - const [expandedDoc, setExpandedDoc] = useState(undefined); const [inspectorSession, setInspectorSession] = useState(undefined); const fetchCounter = useRef(0); @@ -80,6 +92,8 @@ export function DiscoverLayout({ } }, [dataState.fetchStatus]); + useSavedSearchAliasMatchRedirect({ savedSearch, spaces, history }); + const timeField = useMemo(() => { return indexPattern.type !== 'rollup' ? indexPattern.timeFieldName : undefined; }, [indexPattern]); @@ -174,6 +188,11 @@ export function DiscoverLayout({ resetSavedSearch={resetSavedSearch} /> +

{savedSearch.title}

diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx index c68b1ab7b844c..18766b5df7f33 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { SavedObjectSaveModal, showSaveModal } from '../../../../../../../saved_objects/public'; -import { SavedSearch } from '../../../../../saved_searches'; +import { SavedSearch, SaveSavedSearchOptions } from '../../../../../saved_searches'; import { IndexPattern } from '../../../../../../../data/common'; import { DiscoverServices } from '../../../../../build_services'; import { GetStateReturn } from '../../services/discover_state'; @@ -27,11 +27,7 @@ async function saveDataSource({ indexPattern: IndexPattern; navigateTo: (url: string) => void; savedSearch: SavedSearch; - saveOptions: { - confirmOverwrite: boolean; - isTitleDuplicateConfirmed: boolean; - onTitleDuplicate: () => void; - }; + saveOptions: SaveSavedSearchOptions; services: DiscoverServices; state: GetStateReturn; }) { @@ -47,14 +43,20 @@ async function saveDataSource({ }), 'data-test-subj': 'saveSearchSuccess', }); - - if (savedSearch.id !== prevSavedSearchId) { - navigateTo(`/view/${encodeURIComponent(savedSearch.id)}`); + if (id !== prevSavedSearchId) { + navigateTo(`/view/${encodeURIComponent(id)}`); } else { // Update defaults so that "reload saved query" functions correctly state.resetAppState(); - services.chrome.docTitle.change(savedSearch.lastSavedTitle!); - setBreadcrumbsTitle(savedSearch, services.chrome); + services.chrome.docTitle.change(savedSearch.title!); + + setBreadcrumbsTitle( + { + ...savedSearch, + id: prevSavedSearchId ?? id, + }, + services.chrome + ); } } } @@ -106,11 +108,10 @@ export async function onSaveSearch({ }) => { const currentTitle = savedSearch.title; savedSearch.title = newTitle; - savedSearch.copyOnSave = newCopyOnSave; - const saveOptions = { - confirmOverwrite: false, - isTitleDuplicateConfirmed, + const saveOptions: SaveSavedSearchOptions = { onTitleDuplicate, + copyOnSave: newCopyOnSave, + isTitleDuplicateConfirmed, }; const response = await saveDataSource({ indexPattern, @@ -133,7 +134,7 @@ export async function onSaveSearch({ {}} - title={savedSearch.title} + title={savedSearch.title ?? ''} showCopyOnSave={!!savedSearch.id} objectType={i18n.translate('discover.localMenu.saveSaveSearchObjectType', { defaultMessage: 'search', diff --git a/src/plugins/discover/public/application/apps/main/discover_main_route.tsx b/src/plugins/discover/public/application/apps/main/discover_main_route.tsx index a95668642558c..b674bfd6568ac 100644 --- a/src/plugins/discover/public/application/apps/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/apps/main/discover_main_route.tsx @@ -8,15 +8,18 @@ import React, { useEffect, useState, memo } from 'react'; import { History } from 'history'; import { useParams } from 'react-router-dom'; -import type { SavedObject as SavedObjectDeprecated } from 'src/plugins/saved_objects/public'; -import { IndexPatternAttributes, SavedObject } from 'src/plugins/data/common'; +import { i18n } from '@kbn/i18n'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +import { IndexPatternAttributes, ISearchSource, SavedObject } from 'src/plugins/data/common'; import { DiscoverServices } from '../../../build_services'; -import { SavedSearch } from '../../../saved_searches'; +import { SavedSearch, getSavedSearch, getSavedSearchFullPathUrl } from '../../../saved_searches'; import { getState } from './services/discover_state'; import { loadIndexPattern, resolveIndexPattern } from './utils/resolve_index_pattern'; import { DiscoverMainApp } from './discover_main_app'; import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../../helpers/breadcrumbs'; import { redirectWhenMissing } from '../../../../../kibana_utils/public'; +import { DataViewSavedObjectConflictError } from '../../../../../data_views/common'; import { getUrlTracker } from '../../../kibana_services'; import { LoadingIndicator } from '../../components/common/loading_indicator'; @@ -37,6 +40,21 @@ interface DiscoverLandingParams { id: string; } +const DiscoverError = ({ error }: { error: Error }) => ( + + {i18n.translate('discover.discoverError.title', { + defaultMessage: 'Error loading Discover', + })} + + } + body={

{error.message}

} + /> +); + export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { const { core, @@ -46,7 +64,7 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { toastNotifications, http: { basePath }, } = services; - + const [error, setError] = useState(); const [savedSearch, setSavedSearch] = useState(); const indexPattern = savedSearch?.searchSource?.getField('index'); const [indexPatternList, setIndexPatternList] = useState< @@ -58,58 +76,76 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { useEffect(() => { const savedSearchId = id; - async function loadDefaultOrCurrentIndexPattern(usedSavedSearch: SavedSearch) { - await data.indexPatterns.ensureDefaultDataView(); - const { appStateContainer } = getState({ history, uiSettings: config }); - const { index } = appStateContainer.getState(); - const ip = await loadIndexPattern(index || '', data.indexPatterns, config); - const ipList = ip.list as Array>; - const indexPatternData = await resolveIndexPattern( - ip, - usedSavedSearch.searchSource, - toastNotifications - ); - setIndexPatternList(ipList); - return indexPatternData; + async function loadDefaultOrCurrentIndexPattern(searchSource: ISearchSource) { + try { + await data.indexPatterns.ensureDefaultDataView(); + const { appStateContainer } = getState({ history, uiSettings: config }); + const { index } = appStateContainer.getState(); + const ip = await loadIndexPattern(index || '', data.indexPatterns, config); + + const ipList = ip.list as Array>; + const indexPatternData = await resolveIndexPattern(ip, searchSource, toastNotifications); + + setIndexPatternList(ipList); + + return indexPatternData; + } catch (e) { + setError(e); + } } async function loadSavedSearch() { try { - const loadedSavedSearch = await services.getSavedSearchById(savedSearchId); - const loadedIndexPattern = await loadDefaultOrCurrentIndexPattern(loadedSavedSearch); - if (loadedSavedSearch && !loadedSavedSearch?.searchSource.getField('index')) { - loadedSavedSearch.searchSource.setField('index', loadedIndexPattern); + const currentSavedSearch = await getSavedSearch(savedSearchId, { + search: services.data.search, + savedObjectsClient: core.savedObjects.client, + spaces: services.spaces, + }); + + const loadedIndexPattern = await loadDefaultOrCurrentIndexPattern( + currentSavedSearch.searchSource + ); + + if (!currentSavedSearch.searchSource.getField('index')) { + currentSavedSearch.searchSource.setField('index', loadedIndexPattern); } - setSavedSearch(loadedSavedSearch); - if (savedSearchId) { + + setSavedSearch(currentSavedSearch); + + if (currentSavedSearch.id) { chrome.recentlyAccessed.add( - (loadedSavedSearch as unknown as SavedObjectDeprecated).getFullPath(), - loadedSavedSearch.title, - loadedSavedSearch.id + getSavedSearchFullPathUrl(currentSavedSearch.id), + currentSavedSearch.title ?? '', + currentSavedSearch.id ); } } catch (e) { - redirectWhenMissing({ - history, - navigateToApp: core.application.navigateToApp, - basePath, - mapping: { - search: '/', - 'index-pattern': { - app: 'management', - path: `kibana/objects/savedSearches/${id}`, + if (e instanceof DataViewSavedObjectConflictError) { + setError(e); + } else { + redirectWhenMissing({ + history, + navigateToApp: core.application.navigateToApp, + basePath, + mapping: { + search: '/', + 'index-pattern': { + app: 'management', + path: `kibana/objects/savedSearches/${id}`, + }, }, - }, - toastNotifications, - onBeforeRedirect() { - getUrlTracker().setTrackedUrl('/'); - }, - })(e); + toastNotifications, + onBeforeRedirect() { + getUrlTracker().setTrackedUrl('/'); + }, + })(e); + } } } loadSavedSearch(); }, [ + core.savedObjects.client, basePath, chrome.recentlyAccessed, config, @@ -129,6 +165,10 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { ); }, [chrome, savedSearch]); + if (error) { + return ; + } + if (!indexPattern || !savedSearch) { return ; } diff --git a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts index 905d81a6fc716..9968ca6f1f63f 100644 --- a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts @@ -14,7 +14,7 @@ import { } from './discover_state'; import { createBrowserHistory, History } from 'history'; import { dataPluginMock } from '../../../../../../data/public/mocks'; -import { SavedSearch } from '../../../../saved_searches'; +import type { SavedSearch } from '../../../../saved_searches'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common'; let history: History; diff --git a/src/plugins/discover/public/application/apps/main/services/use_discover_state.test.ts b/src/plugins/discover/public/application/apps/main/services/use_discover_state.test.ts index 28f5f96acc144..c719f83980aa0 100644 --- a/src/plugins/discover/public/application/apps/main/services/use_discover_state.test.ts +++ b/src/plugins/discover/public/application/apps/main/services/use_discover_state.test.ts @@ -15,6 +15,20 @@ import { indexPatternMock } from '../../../../__mocks__/index_pattern'; import { SearchSource } from '../../../../../../data/common'; describe('test useDiscoverState', () => { + const originalSavedObjectsClient = discoverServiceMock.core.savedObjects.client; + + beforeAll(() => { + discoverServiceMock.core.savedObjects.client.resolve = jest.fn().mockReturnValue({ + saved_object: { + attributes: {}, + }, + }); + }); + + afterAll(() => { + discoverServiceMock.core.savedObjects.client = originalSavedObjectsClient; + }); + test('return is valid', async () => { const { history } = createSearchSessionMock(); diff --git a/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts b/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts index 223d896b16cd1..ce30c0749b938 100644 --- a/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts +++ b/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts @@ -11,7 +11,7 @@ import { History } from 'history'; import { getState } from './discover_state'; import { getStateDefaults } from '../utils/get_state_defaults'; import { DiscoverServices } from '../../../../build_services'; -import { SavedSearch } from '../../../../saved_searches'; +import { SavedSearch, getSavedSearch } from '../../../../saved_searches'; import { loadIndexPattern } from '../utils/resolve_index_pattern'; import { useSavedSearch as useSavedSearchData } from './use_saved_search'; import { @@ -148,7 +148,12 @@ export function useDiscoverState({ */ const resetSavedSearch = useCallback( async (id?: string) => { - const newSavedSearch = await services.getSavedSearchById(id); + const newSavedSearch = await getSavedSearch(id, { + search: services.data.search, + savedObjectsClient: services.core.savedObjects.client, + spaces: services.spaces, + }); + const newIndexPattern = newSavedSearch.searchSource.getField('index') || indexPattern; newSavedSearch.searchSource.setField('index', newIndexPattern); const newAppState = getStateDefaults({ diff --git a/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts b/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts index d11c76283fedd..6cadfbb89acfb 100644 --- a/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts +++ b/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts @@ -9,7 +9,7 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'; import { BehaviorSubject, Subject } from 'rxjs'; import { DiscoverServices } from '../../../../build_services'; import { DiscoverSearchSessionManager } from './discover_search_session'; -import { SearchSource } from '../../../../../../data/common'; +import { ISearchSource } from '../../../../../../data/common'; import { GetStateReturn } from './discover_state'; import { ElasticSearchHit } from '../../../doc_views/doc_views_types'; import { RequestAdapter } from '../../../../../../inspector/public'; @@ -91,7 +91,7 @@ export const useSavedSearch = ({ }: { initialFetchStatus: FetchStatus; searchSessionManager: DiscoverSearchSessionManager; - searchSource: SearchSource; + searchSource: ISearchSource; services: DiscoverServices; stateContainer: GetStateReturn; useNewFieldsApi: boolean; diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts index 53d13ee547b0f..e9d9335abcda0 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts @@ -14,11 +14,11 @@ import { sendResetMsg, } from '../services/use_saved_search_messages'; import { updateSearchSource } from './update_search_source'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { fetchDocuments } from './fetch_documents'; import { fetchTotalHits } from './fetch_total_hits'; import { fetchChart } from './fetch_chart'; -import { SearchSource } from '../../../../../../data/common'; +import { ISearchSource } from '../../../../../../data/common'; import { Adapters } from '../../../../../../inspector'; import { AppState } from '../services/discover_state'; import { FetchStatus } from '../../../types'; @@ -29,7 +29,7 @@ import { ReduxLikeStateContainer } from '../../../../../../kibana_utils/common'; export function fetchAll( dataSubjects: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, reset = false, fetchDeps: { abortController: AbortController; diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts index 67f34c7503c59..50f3a1b8bfea7 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts @@ -11,7 +11,7 @@ import { DataPublicPluginStart, isCompleteResponse, search, - SearchSource, + ISearchSource, } from '../../../../../../data/public'; import { Adapters } from '../../../../../../inspector'; import { getChartAggConfigs, getDimensions } from './index'; @@ -25,7 +25,7 @@ import { sendErrorMsg, sendLoadingMsg } from '../services/use_saved_search_messa export function fetchChart( data$: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, { abortController, appStateContainer, @@ -114,7 +114,7 @@ export function fetchChart( } export function updateSearchSource( - searchSource: SearchSource, + searchSource: ISearchSource, interval: string, data: DataPublicPluginStart ) { diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts index 2f06a9dbbb3db..6c5eff7cff702 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { filter } from 'rxjs/operators'; import { Adapters } from '../../../../../../inspector/common'; -import { isCompleteResponse, SearchSource } from '../../../../../../data/common'; +import { isCompleteResponse, ISearchSource } from '../../../../../../data/common'; import { FetchStatus } from '../../../types'; import { SavedSearchData } from '../services/use_saved_search'; import { sendErrorMsg, sendLoadingMsg } from '../services/use_saved_search_messages'; @@ -17,7 +17,7 @@ import { DiscoverServices } from '../../../../build_services'; export const fetchDocuments = ( data$: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, { abortController, inspectorAdapters, diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts index 9688f5ddd614d..cfab0d17fcd54 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts @@ -11,7 +11,7 @@ import { filter } from 'rxjs/operators'; import { DataPublicPluginStart, isCompleteResponse, - SearchSource, + ISearchSource, } from '../../../../../../data/public'; import { Adapters } from '../../../../../../inspector/common'; import { FetchStatus } from '../../../types'; @@ -20,7 +20,7 @@ import { sendErrorMsg, sendLoadingMsg } from '../services/use_saved_search_messa export function fetchTotalHits( data$: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, { abortController, data, diff --git a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts index 3eef49fe6ddcb..515565f0062c9 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import { indexPatternWithTimefieldMock } from '../../../../__mocks__/index_pattern_with_timefield'; -import { SearchSource } from '../../../../../../data/public'; +import { ISearchSource } from '../../../../../../data/public'; import { dataPluginMock } from '../../../../../../data/public/mocks'; import { getChartAggConfigs } from './get_chart_agg_configs'; @@ -22,7 +22,7 @@ describe('getChartAggConfigs', () => { } }, removeField: jest.fn(), - } as unknown as SearchSource; + } as unknown as ISearchSource; const dataMock = dataPluginMock.createStartContract(); diff --git a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.ts b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.ts index 2665254027fd9..65f98f72beec0 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.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 { SearchSource } from '../../../../../../data/common'; +import { ISearchSource } from '../../../../../../data/common'; import { DataPublicPluginStart } from '../../../../../../data/public'; /** @@ -13,7 +13,7 @@ import { DataPublicPluginStart } from '../../../../../../data/public'; * for Discover's histogram vis */ export function getChartAggConfigs( - searchSource: SearchSource, + searchSource: ISearchSource, histogramInterval: string, data: DataPublicPluginStart ) { diff --git a/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts b/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts index b98662f2db3b5..35a6e955fe5b2 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts @@ -9,7 +9,7 @@ import { dataPluginMock } from '../../../../../../data/public/mocks'; import { getDimensions } from './get_dimensions'; import { indexPatternWithTimefieldMock } from '../../../../__mocks__/index_pattern_with_timefield'; -import { SearchSource, calculateBounds } from '../../../../../../data/common'; +import { ISearchSource, calculateBounds } from '../../../../../../data/common'; import { getChartAggConfigs } from './get_chart_agg_configs'; test('getDimensions', () => { @@ -23,7 +23,7 @@ test('getDimensions', () => { return indexPattern; } }, - } as unknown as SearchSource; + } as unknown as ISearchSource; const dataMock = dataPluginMock.createStartContract(); dataMock.query.timefilter.timefilter.getTime = () => { diff --git a/src/plugins/discover/public/application/apps/main/utils/get_fetch_observable.ts b/src/plugins/discover/public/application/apps/main/utils/get_fetch_observable.ts index 528f0e74d3ed6..de79a9425f17c 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_fetch_observable.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_fetch_observable.ts @@ -12,7 +12,7 @@ import { FetchStatus } from '../../../types'; import type { AutoRefreshDoneFn, DataPublicPluginStart, - SearchSource, + ISearchSource, } from '../../../../../../data/public'; import { DataMain$, DataRefetch$ } from '../services/use_saved_search'; import { DiscoverSearchSessionManager } from '../services/discover_search_session'; @@ -33,7 +33,7 @@ export function getFetch$({ main$: DataMain$; refetch$: DataRefetch$; searchSessionManager: DiscoverSearchSessionManager; - searchSource: SearchSource; + searchSource: ISearchSource; initialFetchStatus: FetchStatus; }) { const { timefilter } = data.query.timefilter; diff --git a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts index 21292fabdd13f..437d4fda666fc 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts @@ -11,7 +11,7 @@ import type { IUiSettingsClient } from 'src/core/public'; import type { DataPublicPluginStart } from 'src/plugins/data/public'; import type { ISearchSource, SearchSourceFields } from 'src/plugins/data/common'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; -import type { SavedSearch, SortOrder } from '../../../../saved_searches/types'; +import type { SavedSearch, SortOrder } from '../../../../saved_searches'; import { getSortForSearchSource } from '../components/doc_table'; import { AppState } from '../services/discover_state'; diff --git a/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts b/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts index cd23d52022374..11ebf0ecf9af4 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts @@ -31,10 +31,11 @@ export function getStateDefaults({ data: DataPublicPluginStart; savedSearch: SavedSearch; }) { - const searchSource = savedSearch.searchSource; - const indexPattern = savedSearch.searchSource.getField('index'); + const { searchSource } = savedSearch; + const indexPattern = searchSource.getField('index'); + const query = searchSource.getField('query') || data.query.queryString.getDefaultQuery(); - const sort = getSortArray(savedSearch.sort, indexPattern!); + const sort = getSortArray(savedSearch.sort ?? [], indexPattern!); const columns = getDefaultColumns(savedSearch, config); const defaultState = { @@ -43,7 +44,7 @@ export function getStateDefaults({ ? getDefaultSort(indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING, 'desc')) : sort, columns, - index: indexPattern!.id, + index: indexPattern?.id, interval: 'auto', filters: cloneDeep(searchSource.getOwnField('filter')), hideChart: undefined, diff --git a/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts b/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts index a5e1e2bb6c2ea..584fbe14cb59e 100644 --- a/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts +++ b/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts @@ -10,9 +10,10 @@ import { updateSearchSource } from './update_search_source'; import { IndexPattern } from '../../../../../../data/public'; import { SavedSearch } from '../../../../saved_searches'; import { AppState } from '../services/discover_state'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { SavedObjectSaveOpts } from '../../../../../../saved_objects/public'; import { DiscoverServices } from '../../../../build_services'; +import { saveSavedSearch } from '../../../../saved_searches'; /** * Helper function to update and persist the given savedSearch @@ -52,8 +53,10 @@ export async function persistSavedSearch( } try { - const id = await savedSearch.save(saveOptions); - onSuccess(id); + const id = await saveSavedSearch(savedSearch, saveOptions, services.core.savedObjects.client); + if (id) { + onSuccess(id); + } return { id }; } catch (saveError) { onError(saveError, savedSearch); diff --git a/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts b/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts index d30b67db31186..613615446ee08 100644 --- a/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts +++ b/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { IndexPattern, IndexPatternsContract, SearchSource } from 'src/plugins/data/common'; +import type { IndexPattern, IndexPatternsContract, ISearchSource } from 'src/plugins/data/common'; import type { IUiSettingsClient, SavedObject, ToastsStart } from 'kibana/public'; export type IndexPatternSavedObject = SavedObject & { title: string }; @@ -95,7 +95,7 @@ export async function loadIndexPattern( */ export function resolveIndexPattern( ip: IndexPatternData, - searchSource: SearchSource, + searchSource: ISearchSource, toastNotifications: ToastsStart ) { const { loaded: loadedIndexPattern, stateVal, stateValFound } = ip; diff --git a/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts b/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts index 945140e0586ab..22f3b6ad86f6c 100644 --- a/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts @@ -9,7 +9,7 @@ import { updateSearchSource } from './update_search_source'; import { createSearchSourceMock } from '../../../../../../data/common/search/search_source/mocks'; import { indexPatternMock } from '../../../../__mocks__/index_pattern'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { discoverServiceMock } from '../../../../__mocks__/services'; describe('updateSearchSource', () => { diff --git a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts index 4dfcbc7b79712..6d592e176afe5 100644 --- a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts +++ b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts @@ -8,7 +8,7 @@ import { SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; import { IndexPattern, ISearchSource } from '../../../../../../data/common'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { DiscoverServices } from '../../../../build_services'; import { getSortForSearchSource } from '../components/doc_table'; diff --git a/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts index f3edc523f4464..f09131cb5c926 100644 --- a/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts +++ b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts @@ -8,7 +8,7 @@ import { createSearchSourceMock } from '../../../../../data/common/search/search_source/mocks'; import { updateSearchSource } from './update_search_source'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; -import { SortOrder } from '../../../saved_searches/types'; +import type { SortOrder } from '../../../saved_searches'; describe('updateSearchSource', () => { const defaults = { diff --git a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx index ef8670f976672..8849806cf5959 100644 --- a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx @@ -165,7 +165,7 @@ export class SavedSearchEmbeddable const executionContext = { type: this.type, name: 'discover', - id: this.savedSearch.id, + id: this.savedSearch.id!, description: this.output.title || this.output.defaultTitle || '', url: this.output.editUrl, parent: this.input.executionContext, @@ -232,7 +232,7 @@ export class SavedSearchEmbeddable searchDescription: this.savedSearch.description, description: this.savedSearch.description, inspectorAdapters: this.inspectorAdapters, - searchTitle: this.savedSearch.lastSavedTitle, + searchTitle: this.savedSearch.title, services: this.services, onAddColumn: (columnName: string) => { if (!props.columns) { @@ -404,7 +404,6 @@ export class SavedSearchEmbeddable public destroy() { super.destroy(); - this.savedSearch.destroy(); if (this.searchProps) { delete this.searchProps; } diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts index 95f5b2d3ce284..a8b492d368768 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts @@ -20,6 +20,11 @@ import { TimeRange } from '../../../../data/public'; import { SearchInput, SearchOutput } from './types'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; import { SavedSearchEmbeddable } from './saved_search_embeddable'; +import { + getSavedSearch, + getSavedSearchUrl, + throwErrorOnSavedSearchUrlConflict, +} from '../../saved_searches'; interface StartServices { executeTriggerActions: UiActionsStart['executeTriggerActions']; @@ -59,20 +64,27 @@ export class SearchEmbeddableFactory input: Partial & { id: string; timeRange: TimeRange }, parent?: Container ): Promise => { - const filterManager = getServices().filterManager; - - const url = await getServices().getSavedSearchUrlById(savedObjectId); - const editUrl = getServices().addBasePath(`/app/discover${url}`); + const services = getServices(); + const filterManager = services.filterManager; + const url = getSavedSearchUrl(savedObjectId); + const editUrl = services.addBasePath(`/app/discover${url}`); try { - const savedObject = await getServices().getSavedSearchById(savedObjectId); - const indexPattern = savedObject.searchSource.getField('index'); + const savedSearch = await getSavedSearch(savedObjectId, { + search: services.data.search, + savedObjectsClient: services.core.savedObjects.client, + spaces: services.spaces, + }); + + await throwErrorOnSavedSearchUrlConflict(savedSearch); + + const indexPattern = savedSearch.searchSource.getField('index'); const { executeTriggerActions } = await this.getStartServices(); const { SavedSearchEmbeddable: SavedSearchEmbeddableClass } = await import( './saved_search_embeddable' ); return new SavedSearchEmbeddableClass( { - savedSearch: savedObject, + savedSearch, editUrl, editPath: url, filterManager, diff --git a/src/plugins/discover/public/application/embeddable/types.ts b/src/plugins/discover/public/application/embeddable/types.ts index 5a08534918d4f..de109e3fa7879 100644 --- a/src/plugins/discover/public/application/embeddable/types.ts +++ b/src/plugins/discover/public/application/embeddable/types.ts @@ -13,7 +13,7 @@ import { IEmbeddable, } from 'src/plugins/embeddable/public'; import { Filter, IndexPattern, TimeRange, Query } from '../../../../data/public'; -import { SavedSearch } from '../..'; +import { SavedSearch } from '../../saved_searches'; import { SortOrder } from '../apps/main/components/doc_table/components/table_header/helpers'; export interface SearchInput extends EmbeddableInput { diff --git a/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts b/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts index 69c273f326c61..e4b97d011ff64 100644 --- a/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts +++ b/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts @@ -12,6 +12,7 @@ import { IEmbeddable, ViewMode } from '../../../../embeddable/public'; import { Action } from '../../../../ui_actions/public'; import { SavedSearchEmbeddable } from './saved_search_embeddable'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../common'; +import { getSavedSearchUrl } from '../../saved_searches'; export const ACTION_VIEW_SAVED_SEARCH = 'ACTION_VIEW_SAVED_SEARCH'; @@ -28,7 +29,7 @@ export class ViewSavedSearchAction implements Action { async execute(context: ActionExecutionContext): Promise { const { embeddable } = context; const savedSearchId = (embeddable as SavedSearchEmbeddable).getSavedSearch().id; - const path = `#/view/${encodeURIComponent(savedSearchId)}`; + const path = getSavedSearchUrl(savedSearchId); const app = embeddable ? embeddable.getOutput().editApp : undefined; await this.application.navigateToApp(app ? app : 'discover', { path }); } diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index e88f00fadcbf1..ab2484abee892 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -31,13 +31,14 @@ import { UiCounterMetricType } from '@kbn/analytics'; import { Storage } from '../../kibana_utils/public'; import { DiscoverStartPlugins } from './plugin'; -import { createSavedSearchesLoader, SavedSearch } from './saved_searches'; import { getHistory } from './kibana_services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { UrlForwardingStart } from '../../url_forwarding/public'; import { NavigationPublicPluginStart } from '../../navigation/public'; import { IndexPatternFieldEditorStart } from '../../index_pattern_field_editor/public'; +import type { SpacesApi } from '../../../../x-pack/plugins/spaces/public'; + export interface DiscoverServices { addBasePath: (path: string) => string; capabilities: Capabilities; @@ -57,13 +58,12 @@ export interface DiscoverServices { urlForwarding: UrlForwardingStart; timefilter: TimefilterContract; toastNotifications: ToastsStart; - getSavedSearchById: (id?: string) => Promise; - getSavedSearchUrlById: (id: string) => Promise; uiSettings: IUiSettingsClient; trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; indexPatternFieldEditor: IndexPatternFieldEditorStart; http: HttpStart; storage: Storage; + spaces?: SpacesApi; } export function buildServices( @@ -71,11 +71,6 @@ export function buildServices( plugins: DiscoverStartPlugins, context: PluginInitializerContext ): DiscoverServices { - const services = { - savedObjectsClient: core.savedObjects.client, - savedObjects: plugins.savedObjects, - }; - const savedObjectService = createSavedSearchesLoader(services); const { usageCollection } = plugins; const storage = new Storage(localStorage); @@ -88,8 +83,6 @@ export function buildServices( docLinks: core.docLinks, theme: plugins.charts.theme, filterManager: plugins.data.query.filterManager, - getSavedSearchById: async (id?: string) => savedObjectService.get(id), - getSavedSearchUrlById: async (id: string) => savedObjectService.urlFor(id), history: getHistory, indexPatterns: plugins.data.indexPatterns, inspector: plugins.inspector, @@ -107,5 +100,6 @@ export function buildServices( trackUiMetric: usageCollection?.reportUiCounter.bind(usageCollection, 'discover'), indexPatternFieldEditor: plugins.indexPatternFieldEditor, http: core.http, + spaces: plugins.spaces, }; } diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts index 3840df4353faf..f6cd687c962c3 100644 --- a/src/plugins/discover/public/index.ts +++ b/src/plugins/discover/public/index.ts @@ -9,12 +9,23 @@ import { PluginInitializerContext } from 'kibana/public'; import { DiscoverPlugin } from './plugin'; +export { + getSavedSearch, + getSavedSearchFullPathUrl, + getSavedSearchUrl, + getSavedSearchUrlConflictMessage, + throwErrorOnSavedSearchUrlConflict, + SavedSearch, + LegacySavedSearch, + SavedSearchLoader, + __LEGACY, +} from './saved_searches'; + export { DiscoverSetup, DiscoverStart } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new DiscoverPlugin(initializerContext); } -export { SavedSearch, SavedSearchLoader, createSavedSearchesLoader } from './saved_searches'; export { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, SearchInput } from './application/embeddable'; export { loadSharingDataHelpers } from './shared'; diff --git a/src/plugins/discover/public/mocks.ts b/src/plugins/discover/public/mocks.ts index 71de630132b0a..6a3c703ea0da8 100644 --- a/src/plugins/discover/public/mocks.ts +++ b/src/plugins/discover/public/mocks.ts @@ -24,7 +24,9 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { const startContract: Start = { - savedSearchLoader: {} as DiscoverStart['savedSearchLoader'], + __LEGACY: { + savedSearchLoader: {} as DiscoverStart['__LEGACY']['savedSearchLoader'], + }, urlGenerator: { createUrl: jest.fn(), } as unknown as DiscoverStart['urlGenerator'], diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index afb83d6cbd667..e34e7644caa25 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -45,7 +45,7 @@ import { getScopedHistory, syncHistoryLocations, } from './kibana_services'; -import { createSavedSearchesLoader } from './saved_searches'; +import { __LEGACY } from './saved_searches'; import { registerFeature } from './register_feature'; import { buildServices } from './build_services'; import { @@ -61,6 +61,7 @@ import { replaceUrlHashQuery } from '../../kibana_utils/public/'; import { IndexPatternFieldEditorStart } from '../../../plugins/index_pattern_field_editor/public'; import { DeferredSpinner } from './shared'; import { ViewSavedSearchAction } from './application/embeddable/view_saved_search_action'; +import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -120,7 +121,9 @@ export interface DiscoverSetup { } export interface DiscoverStart { - savedSearchLoader: SavedObjectLoader; + __LEGACY: { + savedSearchLoader: SavedObjectLoader; + }; /** * @deprecated Use URL locator instead. URL generator will be removed. @@ -189,6 +192,7 @@ export interface DiscoverStartPlugins { savedObjects: SavedObjectsStart; usageCollection?: UsageCollectionSetup; indexPatternFieldEditor: IndexPatternFieldEditorStart; + spaces?: SpacesPluginStart; } /** @@ -410,10 +414,12 @@ export class DiscoverPlugin return { urlGenerator: this.urlGenerator, locator: this.locator, - savedSearchLoader: createSavedSearchesLoader({ - savedObjectsClient: core.savedObjects.client, - savedObjects: plugins.savedObjects, - }), + __LEGACY: { + savedSearchLoader: __LEGACY.createSavedSearchesLoader({ + savedObjectsClient: core.savedObjects.client, + savedObjects: plugins.savedObjects, + }), + }, }; } diff --git a/src/plugins/discover/public/saved_searches/constants.ts b/src/plugins/discover/public/saved_searches/constants.ts new file mode 100644 index 0000000000000..f8e191c263bd7 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/constants.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** @internal **/ +export const SAVED_SEARCH_TYPE = 'search'; diff --git a/src/plugins/discover/public/saved_searches/get_saved_searches.test.ts b/src/plugins/discover/public/saved_searches/get_saved_searches.test.ts new file mode 100644 index 0000000000000..755831e7009ed --- /dev/null +++ b/src/plugins/discover/public/saved_searches/get_saved_searches.test.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 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 { SavedObjectsStart } from '../../../../core/public'; +import type { DataPublicPluginStart } from '../../../data/public'; + +import { savedObjectsServiceMock } from '../../../../core/public/mocks'; +import { dataPluginMock } from '../../../data/public/mocks'; + +import { getSavedSearch } from './get_saved_searches'; + +describe('getSavedSearch', () => { + let search: DataPublicPluginStart['search']; + let savedObjectsClient: SavedObjectsStart['client']; + + beforeEach(() => { + savedObjectsClient = savedObjectsServiceMock.createStartContract().client; + search = dataPluginMock.createStartContract().search; + }); + + test('should return empty saved search in case of no id', async () => { + const savedSearch = await getSavedSearch(undefined, { savedObjectsClient, search }); + + expect(search.searchSource.createEmpty).toHaveBeenCalled(); + expect(savedSearch).toHaveProperty('searchSource'); + }); + + test('should throw an error if so not found', async () => { + let errorMessage = 'No error thrown.'; + savedObjectsClient.resolve = jest.fn().mockReturnValue({ + saved_object: { + attributes: {}, + error: { + statusCode: 404, + error: 'Not Found', + message: 'Saved object [search/ccf1af80-2297-11ec-86e0-1155ffb9c7a7] not found', + }, + id: 'ccf1af80-2297-11ec-86e0-1155ffb9c7a7', + type: 'search', + references: [], + }, + }); + + try { + await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', { + savedObjectsClient, + search, + }); + } catch (error) { + errorMessage = error.message; + } + + expect(errorMessage).toBe( + 'Could not locate that search (id: ccf1af80-2297-11ec-86e0-1155ffb9c7a7)' + ); + }); + + test('should find saved search', async () => { + savedObjectsClient.resolve = jest.fn().mockReturnValue({ + saved_object: { + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"query":{"query":"","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', + }, + title: 'test1', + sort: [['order_date', 'desc']], + columns: ['_source'], + description: 'description', + grid: {}, + hideChart: false, + }, + id: 'ccf1af80-2297-11ec-86e0-1155ffb9c7a7', + type: 'search', + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + type: 'index-pattern', + }, + ], + namespaces: ['default'], + }, + outcome: 'exactMatch', + }); + + const savedSearch = await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', { + savedObjectsClient, + search, + }); + + expect(savedObjectsClient.resolve).toHaveBeenCalled(); + expect(savedSearch).toMatchInlineSnapshot(` + Object { + "columns": Array [ + "_source", + ], + "description": "description", + "grid": Object {}, + "hideChart": false, + "id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7", + "searchSource": Object { + "create": [MockFunction], + "createChild": [MockFunction], + "createCopy": [MockFunction], + "destroy": [MockFunction], + "fetch": [MockFunction], + "fetch$": [MockFunction], + "getField": [MockFunction], + "getFields": [MockFunction], + "getId": [MockFunction], + "getOwnField": [MockFunction], + "getParent": [MockFunction], + "getSearchRequestBody": [MockFunction], + "getSerializedFields": [MockFunction], + "history": Array [], + "onRequestStart": [MockFunction], + "removeField": [MockFunction], + "serialize": [MockFunction], + "setField": [MockFunction], + "setFields": [MockFunction], + "setParent": [MockFunction], + "setPreferredSearchStrategyId": [MockFunction], + }, + "sharingSavedObjectProps": Object { + "aliasTargetId": undefined, + "errorJSON": undefined, + "outcome": "exactMatch", + }, + "sort": Array [ + Array [ + "order_date", + "desc", + ], + ], + "title": "test1", + } + `); + }); +}); diff --git a/src/plugins/discover/public/saved_searches/get_saved_searches.ts b/src/plugins/discover/public/saved_searches/get_saved_searches.ts new file mode 100644 index 0000000000000..32c50f691fe42 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/get_saved_searches.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 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 { SavedObjectsStart } from '../../../../core/public'; +import type { DataPublicPluginStart } from '../../../data/public'; +import type { SavedSearchAttributes, SavedSearch } from './types'; + +import { SAVED_SEARCH_TYPE } from './constants'; +import { fromSavedSearchAttributes } from './saved_searches_utils'; +import { injectSearchSourceReferences, parseSearchSourceJSON } from '../../../data/public'; +import { SavedObjectNotFound } from '../../../kibana_utils/public'; + +import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public'; + +interface GetSavedSearchDependencies { + search: DataPublicPluginStart['search']; + savedObjectsClient: SavedObjectsStart['client']; + spaces?: SpacesApi; +} + +const getEmptySavedSearch = ({ + search, +}: { + search: DataPublicPluginStart['search']; +}): SavedSearch => ({ + searchSource: search.searchSource.createEmpty(), +}); + +const findSavedSearch = async ( + savedSearchId: string, + { search, savedObjectsClient, spaces }: GetSavedSearchDependencies +) => { + const so = await savedObjectsClient.resolve( + SAVED_SEARCH_TYPE, + savedSearchId + ); + + if (!so.saved_object || so.saved_object.error) { + throw new SavedObjectNotFound(SAVED_SEARCH_TYPE, savedSearchId); + } + + const savedSearch = so.saved_object; + + const parsedSearchSourceJSON = parseSearchSourceJSON( + savedSearch.attributes.kibanaSavedObjectMeta?.searchSourceJSON ?? '{}' + ); + + const searchSourceValues = injectSearchSourceReferences( + parsedSearchSourceJSON as Parameters[0], + savedSearch.references + ); + + return fromSavedSearchAttributes( + savedSearchId, + savedSearch.attributes, + await search.searchSource.create(searchSourceValues), + { + outcome: so.outcome, + aliasTargetId: so.alias_target_id, + errorJSON: + so.outcome === 'conflict' && spaces + ? JSON.stringify({ + targetType: SAVED_SEARCH_TYPE, + sourceId: savedSearchId, + targetSpace: (await spaces.getActiveSpace()).id, + }) + : undefined, + } + ); +}; + +/** @public **/ +export const getSavedSearch = async ( + savedSearchId: string | undefined, + dependencies: GetSavedSearchDependencies +) => { + return savedSearchId + ? findSavedSearch(savedSearchId, dependencies) + : getEmptySavedSearch(dependencies); +}; diff --git a/src/plugins/discover/public/saved_searches/index.ts b/src/plugins/discover/public/saved_searches/index.ts index 86564cc5d6eca..6870fa5e6d617 100644 --- a/src/plugins/discover/public/saved_searches/index.ts +++ b/src/plugins/discover/public/saved_searches/index.ts @@ -6,5 +6,25 @@ * Side Public License, v 1. */ -export { createSavedSearchesLoader } from './saved_searches'; -export { SavedSearch, SavedSearchLoader } from './types'; +import { createSavedSearchesLoader } from './legacy/saved_searches'; + +export { getSavedSearch } from './get_saved_searches'; +export { + getSavedSearchUrl, + getSavedSearchFullPathUrl, + getSavedSearchUrlConflictMessage, + throwErrorOnSavedSearchUrlConflict, +} from './saved_searches_utils'; +export { useSavedSearchAliasMatchRedirect } from './saved_search_alias_match_redirect'; +export { SavedSearchURLConflictCallout } from './saved_search_url_conflict_callout'; +export { saveSavedSearch, SaveSavedSearchOptions } from './save_saved_searches'; + +export { SAVED_SEARCH_TYPE } from './constants'; + +export type { SavedSearch } from './types'; +export type { LegacySavedSearch, SavedSearchLoader, SortOrder } from './legacy/types'; + +/** @deprecated __LEGACY object will be removed in v8**/ +export const __LEGACY = { + createSavedSearchesLoader, +}; diff --git a/src/plugins/discover/public/saved_searches/_saved_search.ts b/src/plugins/discover/public/saved_searches/legacy/_saved_search.ts similarity index 80% rename from src/plugins/discover/public/saved_searches/_saved_search.ts rename to src/plugins/discover/public/saved_searches/legacy/_saved_search.ts index 56533ed20b31e..154f91f5582b3 100644 --- a/src/plugins/discover/public/saved_searches/_saved_search.ts +++ b/src/plugins/discover/public/saved_searches/legacy/_saved_search.ts @@ -6,11 +6,14 @@ * Side Public License, v 1. */ -import { SavedObject, SavedObjectsStart } from '../../../saved_objects/public'; +import type { SavedObject, SavedObjectsStart } from '../../../../saved_objects/public'; +import { SAVED_SEARCH_TYPE } from '../constants'; +import { getSavedSearchFullPathUrl } from '../saved_searches_utils'; +/** @deprecated **/ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { class SavedSearch extends savedObjects.SavedObjectClass { - public static type: string = 'search'; + public static type: string = SAVED_SEARCH_TYPE; public static mapping = { title: 'text', description: 'text', @@ -31,7 +34,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { constructor(id: string) { super({ id, - type: 'search', + type: SAVED_SEARCH_TYPE, mapping: { title: 'text', description: 'text', @@ -54,7 +57,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { }); this.showInRecentlyAccessed = true; this.id = id; - this.getFullPath = () => `/app/discover#/view/${String(id)}`; + this.getFullPath = () => getSavedSearchFullPathUrl(String(id)); } } diff --git a/src/plugins/discover/public/saved_searches/legacy/index.ts b/src/plugins/discover/public/saved_searches/legacy/index.ts new file mode 100644 index 0000000000000..0bfed6f57b9f5 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/legacy/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { createSavedSearchesLoader } from './saved_searches'; +export { LegacySavedSearch, SavedSearchLoader } from './types'; diff --git a/src/plugins/discover/public/saved_searches/saved_searches.ts b/src/plugins/discover/public/saved_searches/legacy/saved_searches.ts similarity index 78% rename from src/plugins/discover/public/saved_searches/saved_searches.ts rename to src/plugins/discover/public/saved_searches/legacy/saved_searches.ts index 0fe693dcd2ebe..58bed080d0249 100644 --- a/src/plugins/discover/public/saved_searches/saved_searches.ts +++ b/src/plugins/discover/public/saved_searches/legacy/saved_searches.ts @@ -6,15 +6,17 @@ * Side Public License, v 1. */ -import { SavedObjectsClientContract } from 'kibana/public'; -import { SavedObjectLoader, SavedObjectsStart } from '../../../saved_objects/public'; +import type { SavedObjectsClientContract } from 'kibana/public'; +import { SavedObjectLoader, SavedObjectsStart } from '../../../../saved_objects/public'; import { createSavedSearchClass } from './_saved_search'; +import { getSavedSearchUrl } from '../saved_searches_utils'; interface Services { savedObjectsClient: SavedObjectsClientContract; savedObjects: SavedObjectsStart; } +/** @deprecated **/ export function createSavedSearchesLoader({ savedObjectsClient, savedObjects }: Services) { const SavedSearchClass = createSavedSearchClass(savedObjects); const savedSearchLoader = new SavedObjectLoader(SavedSearchClass, savedObjectsClient); @@ -25,7 +27,7 @@ export function createSavedSearchesLoader({ savedObjectsClient, savedObjects }: nouns: 'saved searches', }; - savedSearchLoader.urlFor = (id: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/'); + savedSearchLoader.urlFor = getSavedSearchUrl; return savedSearchLoader; } diff --git a/src/plugins/discover/public/saved_searches/legacy/types.ts b/src/plugins/discover/public/saved_searches/legacy/types.ts new file mode 100644 index 0000000000000..e55422ff26a7b --- /dev/null +++ b/src/plugins/discover/public/saved_searches/legacy/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ISearchSource } from '../../../../data/public'; +import type { SavedObjectSaveOpts } from '../../../../saved_objects/public'; +import type { DiscoverGridSettings } from '../../application/components/discover_grid/types'; + +export type SortOrder = [string, string]; + +/** @deprecated **/ +export interface LegacySavedSearch { + readonly id: string; + title: string; + searchSource: ISearchSource; + description?: string; + columns: string[]; + sort: SortOrder[]; + grid: DiscoverGridSettings; + destroy: () => void; + save: (saveOptions: SavedObjectSaveOpts) => Promise; + copyOnSave?: boolean; + hideChart?: boolean; +} + +/** @deprecated **/ +export interface SavedSearchLoader { + get: (id: string) => Promise; + urlFor: (id: string) => string; +} diff --git a/src/plugins/discover/public/saved_searches/save_saved_searches.test.ts b/src/plugins/discover/public/saved_searches/save_saved_searches.test.ts new file mode 100644 index 0000000000000..eabbfe7f9419f --- /dev/null +++ b/src/plugins/discover/public/saved_searches/save_saved_searches.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright 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 { SavedObjectsStart } from '../../../../core/public'; + +import { savedObjectsServiceMock } from '../../../../core/public/mocks'; +import { dataPluginMock } from '../../../data/public/mocks'; + +import { saveSavedSearch } from './save_saved_searches'; +import type { SavedSearch } from './types'; + +describe('saveSavedSearch', () => { + let savedObjectsClient: SavedObjectsStart['client']; + let savedSearch: SavedSearch; + + beforeEach(() => { + savedObjectsClient = savedObjectsServiceMock.createStartContract().client; + const searchSource = dataPluginMock.createStartContract().search.searchSource.createEmpty(); + + savedSearch = { + id: 'id', + title: 'title', + searchSource: { + ...searchSource, + serialize: () => ({ + searchSourceJSON: '{}', + references: [], + }), + }, + sharingSavedObjectProps: { + outcome: 'aliasMatch', + }, + } as SavedSearch; + }); + + describe('onTitleDuplicate', () => { + test('should check for title duplicating', async () => { + savedObjectsClient.find = jest.fn().mockReturnValue({ + savedObjects: [{ get: () => 'title' }], + }); + const onTitleDuplicate = jest.fn(); + + await saveSavedSearch( + savedSearch, + { + onTitleDuplicate, + copyOnSave: true, + }, + savedObjectsClient + ); + + expect(onTitleDuplicate).toHaveBeenCalled(); + }); + + test('should not check for title duplicating for saving existing search', async () => { + savedObjectsClient.find = jest.fn().mockReturnValue({ + savedObjects: [{ get: () => 'title' }], + }); + const onTitleDuplicate = jest.fn(); + + await saveSavedSearch( + savedSearch, + { + onTitleDuplicate, + copyOnSave: false, + }, + savedObjectsClient + ); + + expect(onTitleDuplicate).not.toHaveBeenCalled(); + }); + }); + + test('should call savedObjectsClient.create for saving new search', async () => { + delete savedSearch.id; + + await saveSavedSearch(savedSearch, {}, savedObjectsClient); + + expect(savedObjectsClient.create).toHaveBeenCalledWith( + 'search', + { + columns: [], + description: '', + grid: {}, + hideChart: false, + kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, + sort: [], + title: 'title', + }, + { references: [] } + ); + }); + + test('should call savedObjectsClient.update for saving existing search', async () => { + await saveSavedSearch(savedSearch, {}, savedObjectsClient); + + expect(savedObjectsClient.update).toHaveBeenCalledWith( + 'search', + 'id', + { + columns: [], + description: '', + grid: {}, + hideChart: false, + kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, + sort: [], + title: 'title', + }, + { references: [] } + ); + }); +}); diff --git a/src/plugins/discover/public/saved_searches/save_saved_searches.ts b/src/plugins/discover/public/saved_searches/save_saved_searches.ts new file mode 100644 index 0000000000000..c3440bdb7696a --- /dev/null +++ b/src/plugins/discover/public/saved_searches/save_saved_searches.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { SavedObjectsStart } from 'kibana/public'; +import type { SavedSearch, SavedSearchAttributes } from './types'; + +import { SAVED_SEARCH_TYPE } from './constants'; +import { toSavedSearchAttributes } from './saved_searches_utils'; + +export interface SaveSavedSearchOptions { + onTitleDuplicate?: () => void; + isTitleDuplicateConfirmed?: boolean; + copyOnSave?: boolean; +} + +const hasDuplicatedTitle = async ( + title: string, + savedObjectsClient: SavedObjectsStart['client'] +): Promise => { + if (!title) { + return; + } + + const response = await savedObjectsClient.find({ + type: SAVED_SEARCH_TYPE, + perPage: 10, + search: `"${title}"`, + searchFields: ['title'], + fields: ['title'], + }); + + return response.savedObjects.some( + (obj) => obj.get('title').toLowerCase() === title.toLowerCase() + ); +}; + +/** @internal **/ +export const saveSavedSearch = async ( + savedSearch: SavedSearch, + options: SaveSavedSearchOptions, + savedObjectsClient: SavedObjectsStart['client'] +): Promise => { + const isNew = options.copyOnSave || !savedSearch.id; + + if (savedSearch.title) { + if ( + isNew && + !options.isTitleDuplicateConfirmed && + options.onTitleDuplicate && + (await hasDuplicatedTitle(savedSearch.title, savedObjectsClient)) + ) { + options.onTitleDuplicate(); + return; + } + } + + const { searchSourceJSON, references } = savedSearch.searchSource.serialize(); + const resp = isNew + ? await savedObjectsClient.create( + SAVED_SEARCH_TYPE, + toSavedSearchAttributes(savedSearch, searchSourceJSON), + { + references, + } + ) + : await savedObjectsClient.update( + SAVED_SEARCH_TYPE, + savedSearch.id!, + toSavedSearchAttributes(savedSearch, searchSourceJSON), + { + references, + } + ); + + return resp?.id; +}; diff --git a/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.test.ts b/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.test.ts new file mode 100644 index 0000000000000..0a871061d2b19 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import type { History } from 'history'; + +import { useSavedSearchAliasMatchRedirect } from './saved_search_alias_match_redirect'; +import type { SavedSearch } from './types'; + +import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks'; + +describe('useSavedSearchAliasMatchRedirect', () => { + let spaces: ReturnType; + let history: () => History; + + beforeEach(() => { + spaces = spacesPluginMock.createStartContract(); + history = () => + ({ + location: { + search: '?_g=foo', + }, + } as History); + }); + + test('should redirect in case of aliasMatch', () => { + const savedSearch = { + id: 'id', + sharingSavedObjectProps: { + outcome: 'aliasMatch', + aliasTargetId: 'aliasTargetId', + }, + } as SavedSearch; + + renderHook(() => useSavedSearchAliasMatchRedirect({ spaces, savedSearch, history })); + + expect(spaces.ui.redirectLegacyUrl).toHaveBeenCalledWith( + '#/view/aliasTargetId?_g=foo', + ' search' + ); + }); + + test('should not redirect if outcome !== aliasMatch', () => { + const savedSearch = { + id: 'id', + sharingSavedObjectProps: { + outcome: 'exactMatch', + }, + } as SavedSearch; + + renderHook(() => useSavedSearchAliasMatchRedirect({ spaces, savedSearch, history })); + + expect(spaces.ui.redirectLegacyUrl).not.toHaveBeenCalled(); + }); + + test('should not redirect if aliasTargetId is not defined', () => { + const savedSearch = { + id: 'id', + sharingSavedObjectProps: { + outcome: 'aliasMatch', + }, + } as SavedSearch; + + renderHook(() => useSavedSearchAliasMatchRedirect({ spaces, savedSearch, history })); + + expect(spaces.ui.redirectLegacyUrl).not.toHaveBeenCalled(); + }); +}); diff --git a/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.ts b/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.ts new file mode 100644 index 0000000000000..3a88c1a2b1989 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.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 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 { History } from 'history'; +import { useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { getSavedSearchUrl } from './saved_searches_utils'; + +import type { SavedSearch } from './types'; +import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public'; + +interface SavedSearchAliasMatchRedirectProps { + savedSearch?: SavedSearch; + spaces?: SpacesApi; + history: () => History; +} + +export const useSavedSearchAliasMatchRedirect = ({ + savedSearch, + spaces, + history, +}: SavedSearchAliasMatchRedirectProps) => { + useEffect(() => { + async function aliasMatchRedirect() { + if (savedSearch) { + const { aliasTargetId, outcome } = savedSearch.sharingSavedObjectProps ?? {}; + + if (spaces && aliasTargetId && outcome === 'aliasMatch') { + await spaces.ui.redirectLegacyUrl( + `${getSavedSearchUrl(aliasTargetId)}${history().location.search}`, + i18n.translate('discover.savedSearchAliasMatchRedirect.objectNoun', { + defaultMessage: '{savedSearch} search', + values: { + savedSearch: savedSearch.title, + }, + }) + ); + } + } + } + + aliasMatchRedirect(); + }, [savedSearch, spaces, history]); +}; diff --git a/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.test.tsx b/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.test.tsx new file mode 100644 index 0000000000000..c92c15e771f64 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.test.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 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 type { History } from 'history'; + +import { mountWithIntl } from '@kbn/test/jest'; +import { SavedSearchURLConflictCallout } from './saved_search_url_conflict_callout'; +import type { SavedSearch } from './types'; + +import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks'; + +describe('SavedSearchURLConflictCallout', () => { + let spaces: ReturnType; + let history: () => History; + + beforeEach(() => { + spaces = spacesPluginMock.createStartContract(); + spaces.ui.components.getLegacyUrlConflict = jest.fn().mockReturnValue('callout'); + history = () => + ({ + location: { + search: '?_g=foo', + }, + } as History); + }); + + test("should render URLConflictCallout in case of id's conflicts", () => { + const savedSearch = { + id: 'id', + sharingSavedObjectProps: { + outcome: 'conflict', + aliasTargetId: 'aliasTargetId', + }, + } as SavedSearch; + + const component = mountWithIntl( + + ); + + expect(component.children()).toMatchInlineSnapshot(`"callout"`); + }); + + test('should not render URLConflictCallout in case of no conflicts', () => { + const savedSearch = { + id: 'id', + sharingSavedObjectProps: {}, + } as SavedSearch; + + const component = mountWithIntl( + + ); + + expect(component.children()).toMatchInlineSnapshot(`null`); + }); +}); diff --git a/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts b/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts new file mode 100644 index 0000000000000..fd07126c496cf --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { i18n } from '@kbn/i18n'; +import type { History } from 'history'; +import { getSavedSearchUrl } from './saved_searches_utils'; + +import type { SavedSearch } from './types'; +import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public'; + +interface SavedSearchURLConflictCalloutProps { + savedSearch?: SavedSearch; + spaces?: SpacesApi; + history: () => History; +} + +export const SavedSearchURLConflictCallout = ({ + savedSearch, + spaces, + history, +}: SavedSearchURLConflictCalloutProps) => { + if (spaces && savedSearch?.id && savedSearch?.sharingSavedObjectProps?.outcome === 'conflict') { + const otherObjectId = savedSearch.sharingSavedObjectProps?.aliasTargetId; + + if (otherObjectId) { + return spaces.ui.components.getLegacyUrlConflict({ + objectNoun: i18n.translate('discover.savedSearchURLConflictCallout.objectNoun', { + defaultMessage: '{savedSearch} search', + values: { + savedSearch: savedSearch.title, + }, + }), + currentObjectId: savedSearch.id, + otherObjectPath: `${getSavedSearchUrl(otherObjectId)}${history().location.search}`, + otherObjectId, + }); + } + } + + return null; +}; diff --git a/src/plugins/discover/public/saved_searches/saved_searches_utils.test.ts b/src/plugins/discover/public/saved_searches/saved_searches_utils.test.ts new file mode 100644 index 0000000000000..12c73e86b3dc4 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_searches_utils.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + getSavedSearchUrl, + getSavedSearchFullPathUrl, + fromSavedSearchAttributes, + toSavedSearchAttributes, + throwErrorOnSavedSearchUrlConflict, +} from './saved_searches_utils'; + +import { createSearchSourceMock } from '../../../data/public/mocks'; + +import type { SavedSearchAttributes, SavedSearch } from './types'; + +describe('saved_searches_utils', () => { + describe('getSavedSearchUrl', () => { + test('should return valid saved search url', () => { + expect(getSavedSearchUrl()).toBe('#/'); + expect(getSavedSearchUrl('id')).toBe('#/view/id'); + }); + }); + + describe('getSavedSearchFullPathUrl', () => { + test('should return valid full path url', () => { + expect(getSavedSearchFullPathUrl()).toBe('/app/discover#/'); + expect(getSavedSearchFullPathUrl('id')).toBe('/app/discover#/view/id'); + }); + }); + + describe('fromSavedSearchAttributes', () => { + test('should convert attributes into SavedSearch', () => { + const attributes: SavedSearchAttributes = { + kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, + title: 'saved search', + sort: [], + columns: ['a', 'b'], + description: 'foo', + grid: {}, + hideChart: true, + }; + + expect(fromSavedSearchAttributes('id', attributes, createSearchSourceMock(), {})) + .toMatchInlineSnapshot(` + Object { + "columns": Array [ + "a", + "b", + ], + "description": "foo", + "grid": Object {}, + "hideChart": true, + "id": "id", + "searchSource": SearchSource { + "dependencies": Object { + "getConfig": [MockFunction], + "onResponse": [MockFunction], + "search": [MockFunction], + }, + "fields": Object {}, + "getFieldName": [Function], + "history": Array [], + "id": "data_source1", + "inheritOptions": Object {}, + "parent": undefined, + "requestStartHandlers": Array [], + "searchStrategyId": undefined, + }, + "sharingSavedObjectProps": Object {}, + "sort": Array [], + "title": "saved search", + } + `); + }); + }); + + describe('throwErrorOnSavedSearchUrlConflict', () => { + test('should throw an error on url conflict', async () => { + let error = 'no error'; + + try { + await throwErrorOnSavedSearchUrlConflict({ + id: 'id', + sharingSavedObjectProps: { + outcome: 'conflict', + errorJSON: '{}', + }, + } as SavedSearch); + } catch (e) { + error = e.message; + } + + expect(error).toBe( + 'This search has the same URL as a legacy alias. Disable the alias to resolve this error : {}' + ); + }); + }); + + describe('toSavedSearchAttributes', () => { + test('should serialize SavedSearch attributes', () => { + const savedSearch: SavedSearch = { + id: 'id', + searchSource: createSearchSourceMock(), + title: 'title', + sort: [['a', 'asc']], + columns: ['c', 'd'], + description: 'description', + grid: {}, + hideChart: true, + }; + + expect(toSavedSearchAttributes(savedSearch, '{}')).toMatchInlineSnapshot(` + Object { + "columns": Array [ + "c", + "d", + ], + "description": "description", + "grid": Object {}, + "hideChart": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "sort": Array [ + Array [ + "a", + "asc", + ], + ], + "title": "title", + } + `); + }); + }); +}); diff --git a/src/plugins/discover/public/saved_searches/saved_searches_utils.ts b/src/plugins/discover/public/saved_searches/saved_searches_utils.ts new file mode 100644 index 0000000000000..98ab2267a875e --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_searches_utils.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 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 { i18n } from '@kbn/i18n'; +import type { SavedSearchAttributes, SavedSearch } from './types'; + +export const getSavedSearchUrl = (id?: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/'); + +export const getSavedSearchFullPathUrl = (id?: string) => `/app/discover${getSavedSearchUrl(id)}`; + +export const getSavedSearchUrlConflictMessage = async (savedSearch: SavedSearch) => + i18n.translate('discover.savedSearchEmbeddable.legacyURLConflict.errorMessage', { + defaultMessage: `This search has the same URL as a legacy alias. Disable the alias to resolve this error : {json}`, + values: { + json: savedSearch.sharingSavedObjectProps?.errorJSON, + }, + }); + +export const throwErrorOnSavedSearchUrlConflict = async (savedSearch: SavedSearch) => { + if (savedSearch.sharingSavedObjectProps?.errorJSON) { + throw new Error(await getSavedSearchUrlConflictMessage(savedSearch)); + } +}; + +export const fromSavedSearchAttributes = ( + id: string, + attributes: SavedSearchAttributes, + searchSource: SavedSearch['searchSource'], + sharingSavedObjectProps: SavedSearch['sharingSavedObjectProps'] +): SavedSearch => ({ + id, + searchSource, + sharingSavedObjectProps, + title: attributes.title, + sort: attributes.sort, + columns: attributes.columns, + description: attributes.description, + grid: attributes.grid, + hideChart: attributes.hideChart, +}); + +export const toSavedSearchAttributes = ( + savedSearch: SavedSearch, + searchSourceJSON: string +): SavedSearchAttributes => ({ + kibanaSavedObjectMeta: { searchSourceJSON }, + title: savedSearch.title ?? '', + sort: savedSearch.sort ?? [], + columns: savedSearch.columns ?? [], + description: savedSearch.description ?? '', + grid: savedSearch.grid ?? {}, + hideChart: savedSearch.hideChart ?? false, +}); diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index b1c7b48d696b3..645ada901d5e5 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -6,26 +6,39 @@ * Side Public License, v 1. */ -import { SearchSource } from '../../../data/public'; -import { SavedObjectSaveOpts } from '../../../saved_objects/public'; -import { DiscoverGridSettings } from '../application/components/discover_grid/types'; +import type { ISearchSource } from '../../../data/public'; +import { DiscoverGridSettingsColumn } from '../application/components/discover_grid/types'; -export type SortOrder = [string, string]; -export interface SavedSearch { - readonly id: string; +/** @internal **/ +export interface SavedSearchAttributes { title: string; - searchSource: SearchSource; - description?: string; + sort: Array<[string, string]>; columns: string[]; - sort: SortOrder[]; - grid: DiscoverGridSettings; - destroy: () => void; - save: (saveOptions: SavedObjectSaveOpts) => Promise; - lastSavedTitle?: string; - copyOnSave?: boolean; - hideChart?: boolean; + description: string; + grid: { + columns?: Record; + }; + hideChart: boolean; + kibanaSavedObjectMeta: { + searchSourceJSON: string; + }; } -export interface SavedSearchLoader { - get: (id: string) => Promise; - urlFor: (id: string) => string; + +/** @public **/ +export interface SavedSearch { + searchSource: ISearchSource; + id?: string; + title?: string; + sort?: Array<[string, string]>; + columns?: string[]; + description?: string; + grid?: { + columns?: Record; + }; + hideChart?: boolean; + sharingSavedObjectProps?: { + outcome?: 'aliasMatch' | 'exactMatch' | 'conflict'; + aliasTargetId?: string; + errorJSON?: string; + }; } diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index b3f1ad5d0bc1e..eb739e673cacd 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -24,6 +24,8 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../kibana_legacy/tsconfig.json" }, { "path": "../index_pattern_field_editor/tsconfig.json"}, - { "path": "../field_formats/tsconfig.json" } + { "path": "../field_formats/tsconfig.json" }, + { "path": "../data_views/tsconfig.json" }, + { "path": "../../../x-pack/plugins/spaces/tsconfig.json" } ] } diff --git a/src/plugins/vis_default_editor/kibana.json b/src/plugins/vis_default_editor/kibana.json index e85c5713eb82c..efed1eab1e494 100644 --- a/src/plugins/vis_default_editor/kibana.json +++ b/src/plugins/vis_default_editor/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "ui": true, "optionalPlugins": ["visualize"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "data", "fieldFormats"], + "requiredBundles": ["kibanaUtils", "kibanaReact", "data", "fieldFormats", "discover"], "owner": { "name": "Vis Editors", "githubTeam": "kibana-vis-editors" diff --git a/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx index dab982e5a8070..f1eebbbdf2116 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx @@ -26,7 +26,7 @@ import { } from 'src/plugins/visualizations/public'; import type { Schema } from 'src/plugins/visualizations/public'; import { TimeRange } from 'src/plugins/data/public'; -import { SavedObject } from 'src/plugins/saved_objects/public'; +import { SavedSearch } from 'src/plugins/discover/public'; import { DefaultEditorNavBar } from './navbar'; import { DefaultEditorControls } from './controls'; import { setStateParamValue, useEditorReducer, useEditorFormState, discardChanges } from './state'; @@ -42,7 +42,7 @@ interface DefaultEditorSideBarProps { vis: Vis; isLinkedSearch: boolean; eventEmitter: EventEmitter; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; timeRange: TimeRange; } diff --git a/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx b/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx index cab27d53b827d..2740f4ff50b4e 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx @@ -25,18 +25,18 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { Vis } from 'src/plugins/visualizations/public'; -import { SavedObject } from 'src/plugins/saved_objects/public'; +import { SavedSearch, getSavedSearchUrl } from '../../../../discover/public'; import { ApplicationStart } from '../../../../../core/public'; import { useKibana } from '../../../../kibana_react/public'; interface LinkedSearchProps { - savedSearch: SavedObject; + savedSearch: SavedSearch; eventEmitter: EventEmitter; } interface SidebarTitleProps { isLinkedSearch: boolean; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; vis: Vis; eventEmitter: EventEmitter; } @@ -55,7 +55,7 @@ export function LinkedSearch({ savedSearch, eventEmitter }: LinkedSearchProps) { }, [eventEmitter]); const onClickViewInDiscover = useCallback(() => { application.navigateToApp('discover', { - path: `#/view/${savedSearch.id}`, + path: getSavedSearchUrl(savedSearch.id), }); }, [application, savedSearch.id]); diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 47f544ce2f5d3..87095f5c389ed 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -23,9 +23,9 @@ import { setAggs, setChrome, setOverlays, - setSavedSearchLoader, setEmbeddable, setDocLinks, + setSpaces, } from './services'; import { VISUALIZE_EMBEDDABLE_TYPE, @@ -51,8 +51,6 @@ import { findListItems, } from './utils/saved_visualize_utils'; -import { createSavedSearchesLoader } from '../../discover/public'; - import type { PluginInitializerContext, CoreSetup, @@ -191,6 +189,11 @@ export class VisualizationsPlugin setAggs(data.search.aggs); setOverlays(core.overlays); setChrome(core.chrome); + + if (spaces) { + setSpaces(spaces); + } + const savedVisualizationsLoader = createSavedVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, @@ -198,11 +201,7 @@ export class VisualizationsPlugin visualizationTypes: types, }); setSavedVisualizationsLoader(savedVisualizationsLoader); - const savedSearchLoader = createSavedSearchesLoader({ - savedObjectsClient: core.savedObjects.client, - savedObjects, - }); - setSavedSearchLoader(savedSearchLoader); + return { ...types, showNewVisModal, diff --git a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts index fbd8e414c2738..aa8183eb8da39 100644 --- a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts +++ b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts @@ -16,8 +16,8 @@ import type { SavedObjectsStart, SavedObject } from '../../../../plugins/saved_objects/public'; // @ts-ignore import { updateOldState } from '../legacy/vis_update_state'; +import { __LEGACY } from '../../../discover/public'; import { extractReferences, injectReferences } from '../utils/saved_visualization_references'; -import { createSavedSearchesLoader } from '../../../discover/public'; import type { SavedObjectsClientContract } from '../../../../core/public'; import type { IndexPatternsContract } from '../../../../plugins/data/public'; import type { ISavedVis } from '../types'; @@ -30,7 +30,7 @@ export interface SavedVisServices { /** @deprecated **/ export function createSavedVisClass(services: SavedVisServices) { - const savedSearch = createSavedSearchesLoader(services); + const savedSearch = __LEGACY.createSavedSearchesLoader(services); class SavedVis extends services.savedObjects.SavedObjectClass { public static type: string = 'visualization'; diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index f1ab9077cd207..b5db56e61ebe4 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -18,13 +18,14 @@ import type { } from '../../../core/public'; import type { TypesStart } from './vis_types'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; -import { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public'; -import { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; -import { ExpressionsStart } from '../../../plugins/expressions/public'; -import { UiActionsStart } from '../../../plugins/ui_actions/public'; -import { SavedVisualizationsLoader } from './saved_visualizations'; -import { SavedObjectLoader } from '../../saved_objects/public'; -import { EmbeddableStart } from '../../embeddable/public'; +import type { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public'; +import type { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; +import type { ExpressionsStart } from '../../../plugins/expressions/public'; +import type { UiActionsStart } from '../../../plugins/ui_actions/public'; +import type { SavedVisualizationsLoader } from './saved_visualizations'; +import type { EmbeddableStart } from '../../embeddable/public'; + +import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -64,5 +65,4 @@ export const [getOverlays, setOverlays] = createGetterSetter('Over export const [getChrome, setChrome] = createGetterSetter('Chrome'); -export const [getSavedSearchLoader, setSavedSearchLoader] = - createGetterSetter('savedSearchLoader'); +export const [getSpaces, setSpaces] = createGetterSetter('Spaces', false); diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts index dfab4ecfc3cd8..2a1e7f2c8c673 100644 --- a/src/plugins/visualizations/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -21,17 +21,19 @@ import { Assign } from '@kbn/utility-types'; import { i18n } from '@kbn/i18n'; import { PersistedState } from './persisted_state'; -import { getTypes, getAggs, getSearch, getSavedSearchLoader } from './services'; +import { getTypes, getAggs, getSearch, getSavedObjects, getSpaces } from './services'; import { IAggConfigs, IndexPattern, ISearchSource, AggConfigSerialized, SearchSourceFields, -} from '../../../plugins/data/public'; +} from '../../data/public'; import { BaseVisType } from './vis_types'; import { VisParams } from '../common/types'; +import { getSavedSearch, throwErrorOnSavedSearchUrlConflict } from '../../discover/public'; + export interface SerializedVisData { expression?: string; aggs: AggConfigSerialized[]; @@ -58,14 +60,20 @@ export interface VisData { } const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => { - const searchSource = inputSearchSource.createCopy(); if (savedSearchId) { - const savedSearch = await getSavedSearchLoader().get(savedSearchId); + const savedSearch = await getSavedSearch(savedSearchId, { + search: getSearch(), + savedObjectsClient: getSavedObjects().client, + spaces: getSpaces(), + }); + + await throwErrorOnSavedSearchUrlConflict(savedSearch); - searchSource.setParent(savedSearch.searchSource); + if (savedSearch?.searchSource) { + inputSearchSource.setParent(savedSearch.searchSource); + } } - searchSource.setField('size', 0); - return searchSource; + return inputSearchSource; }; type PartialVisState = Assign }>; diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts index 4debd9a4a7b7d..e77520c962d88 100644 --- a/src/plugins/visualize/public/application/types.ts +++ b/src/plugins/visualize/public/application/types.ts @@ -8,7 +8,6 @@ import type { EventEmitter } from 'events'; import type { History } from 'history'; - import type { SerializableRecord } from '@kbn/utility-types'; import type { @@ -38,7 +37,7 @@ import type { import type { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public'; import type { Query, Filter, DataPublicPluginStart, TimeRange } from 'src/plugins/data/public'; import type { SharePluginStart } from 'src/plugins/share/public'; -import type { SavedObjectsStart, SavedObject } from 'src/plugins/saved_objects/public'; +import type { SavedObjectsStart } from 'src/plugins/saved_objects/public'; import type { EmbeddableStart, EmbeddableStateTransfer } from 'src/plugins/embeddable/public'; import type { UrlForwardingStart } from 'src/plugins/url_forwarding/public'; import type { PresentationUtilPluginStart } from 'src/plugins/presentation_util/public'; @@ -46,6 +45,7 @@ import type { SpacesPluginStart } from '../../../../../x-pack/plugins/spaces/pub import type { DashboardStart } from '../../../dashboard/public'; import type { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public'; import type { UsageCollectionStart } from '../../../usage_collection/public'; +import type { SavedSearch } from '../../../discover/public'; import { PureVisState } from '../../common/types'; @@ -108,20 +108,15 @@ export interface VisualizeServices extends CoreStart { spaces?: SpacesPluginStart; } -export interface SavedVisInstance { - vis: Vis; - savedVis: VisSavedObject; - savedSearch?: SavedObject; - embeddableHandler: VisualizeEmbeddableContract; -} - -export interface ByValueVisInstance { +export interface VisInstance { vis: Vis; savedVis: VisSavedObject; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; embeddableHandler: VisualizeEmbeddableContract; } +export type SavedVisInstance = VisInstance; +export type ByValueVisInstance = VisInstance; export type VisualizeEditorVisInstance = SavedVisInstance | ByValueVisInstance; export type VisEditorConstructor = new ( @@ -142,7 +137,7 @@ export interface EditorRenderProps { filters: Filter[]; timeRange: TimeRange; query?: Query; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; uiState: PersistedState; /** * Flag to determine if visualiztion is linked to the saved search diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts index 209516793d69d..777ba244c06a1 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { createSavedSearchesLoader } from '../../../../discover/public'; +import { getSavedSearch } from '../../../../discover/public'; import type { VisualizeInput, VisSavedObject, @@ -21,13 +21,13 @@ import { createVisualizeServicesMock } from './mocks'; import { VisualizeServices } from '../types'; import { BehaviorSubject } from 'rxjs'; -const mockSavedSearchObj = {}; -const mockGetSavedSearch = jest.fn(() => mockSavedSearchObj); - jest.mock('../../../../discover/public', () => ({ - createSavedSearchesLoader: jest.fn(() => ({ - get: mockGetSavedSearch, - })), + getSavedSearch: jest.fn().mockResolvedValue({ + id: 'savedSearch', + title: 'savedSearchTitle', + searchSource: {}, + }), + throwErrorOnSavedSearchUrlConflict: jest.fn(), })); let savedVisMock: VisSavedObject; @@ -116,9 +116,14 @@ describe('getVisualizationInstance', () => { visMock.data.savedSearchId = 'saved_search_id'; const { savedSearch } = await getVisualizationInstance(mockServices, 'saved_vis_id'); - expect(createSavedSearchesLoader).toHaveBeenCalled(); - expect(mockGetSavedSearch).toHaveBeenCalledWith(visMock.data.savedSearchId); - expect(savedSearch).toBe(mockSavedSearchObj); + expect(getSavedSearch).toHaveBeenCalled(); + expect(savedSearch).toMatchInlineSnapshot(` + Object { + "id": "savedSearch", + "searchSource": Object {}, + "title": "savedSearchTitle", + } + `); }); test('should subscribe on embeddable handler updates and send toasts on errors', async () => { diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index faf25ff28cec0..876501d5f099b 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -14,10 +14,13 @@ import { VisualizeInput, } from 'src/plugins/visualizations/public'; import { SearchSourceFields } from 'src/plugins/data/public'; -import { SavedObject } from 'src/plugins/saved_objects/public'; import { cloneDeep } from 'lodash'; import { ExpressionValueError } from 'src/plugins/expressions/public'; -import { createSavedSearchesLoader } from '../../../../discover/public'; +import { + getSavedSearch, + SavedSearch, + throwErrorOnSavedSearchUrlConflict, +} from '../../../../discover/public'; import { SavedFieldNotFound, SavedFieldTypeInvalidForAgg } from '../../../../kibana_utils/common'; import { VisualizeServices } from '../types'; @@ -33,8 +36,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( vis: Vis, visualizeServices: VisualizeServices ) => { - const { data, createVisEmbeddableFromObject, savedObjects, savedObjectsPublic } = - visualizeServices; + const { data, createVisEmbeddableFromObject, savedObjects, spaces } = visualizeServices; const embeddableHandler = (await createVisEmbeddableFromObject(vis, { id: '', timeRange: data.query.timefilter.timefilter.getTime(), @@ -50,13 +52,16 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( } }); - let savedSearch: SavedObject | undefined; + let savedSearch: SavedSearch | undefined; if (vis.data.savedSearchId) { - savedSearch = await createSavedSearchesLoader({ + savedSearch = await getSavedSearch(vis.data.savedSearchId, { + search: data.search, savedObjectsClient: savedObjects.client, - savedObjects: savedObjectsPublic, - }).get(vis.data.savedSearchId); + spaces, + }); + + await throwErrorOnSavedSearchUrlConflict(savedSearch); } return { savedSearch, embeddableHandler }; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index c9df6a6ec57d8..7ff3434286b6b 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -28,7 +28,6 @@ import { createKbnUrlStateStorage, withNotifyOnErrors, } from '../../kibana_utils/public'; -import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; import { VisualizeConstants } from './application/visualize_constants'; import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; @@ -45,6 +44,7 @@ import type { EmbeddableStart } from '../../embeddable/public'; import type { DashboardStart } from '../../dashboard/public'; import type { SavedObjectTaggingOssPluginStart } from '../../saved_objects_tagging_oss/public'; import type { UsageCollectionStart } from '../../usage_collection/public'; +import type { SpacesApi } from '../../../../x-pack/plugins/spaces/public'; import { setVisEditorsRegistry, setUISettings, setUsageCollector } from './services'; import { createVisEditorsRegistry, VisEditorsRegistry } from './vis_editors_registry'; @@ -62,7 +62,7 @@ export interface VisualizePluginStartDependencies { savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; presentationUtil: PresentationUtilPluginStart; usageCollection?: UsageCollectionStart; - spaces: SpacesPluginStart; + spaces?: SpacesApi; } export interface VisualizePluginSetupDependencies { diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts index bedf310725ae2..a4fe1835ce5f7 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts @@ -107,7 +107,7 @@ describe('GetCsvReportPanelAction', () => { columns: [], objectType: 'downloadCsv', searchSource: {}, - title: undefined, + title: '', version: '7.15.0', }); }); @@ -144,7 +144,7 @@ describe('GetCsvReportPanelAction', () => { columns: ['column_a', 'column_b'], objectType: 'downloadCsv', searchSource: { testData: 'testDataValue' }, - title: undefined, + title: '', version: '7.15.0', }); }); diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index 115d7599c6bc8..fbfaeab9bc4f2 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -122,7 +122,7 @@ export class ReportingCsvPanelAction implements ActionDefinition const immediateJobParams = this.apiClient.getDecoratedJobParams({ searchSource: getSearchSource(true), columns, - title: savedSearch.title, + title: savedSearch.title || '', objectType: 'downloadCsv', // FIXME: added for typescript, but immediate download job does not need objectType }); diff --git a/x-pack/plugins/transform/kibana.json b/x-pack/plugins/transform/kibana.json index 5e1c1fb938a86..f47b018dd1d26 100644 --- a/x-pack/plugins/transform/kibana.json +++ b/x-pack/plugins/transform/kibana.json @@ -17,6 +17,7 @@ "optionalPlugins": [ "security", "usageCollection", + "spaces", "alerting" ], "configPath": ["xpack", "transform"], diff --git a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts index ae072e6666e4a..e0788d7747e65 100644 --- a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts +++ b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts @@ -13,7 +13,7 @@ export const useRequest = jest.fn(() => ({ error: null, data: undefined, })); -export const createSavedSearchesLoader = jest.fn(); +export const getSavedSearch = jest.fn(); // just passing through the reimports export { getMlSharedImports, ES_CLIENT_TOTAL_HITS_RELATION } from '../../../ml/public'; diff --git a/x-pack/plugins/transform/public/app/app_dependencies.tsx b/x-pack/plugins/transform/public/app/app_dependencies.tsx index c39aa5a49e5e9..d3f356f3e83b3 100644 --- a/x-pack/plugins/transform/public/app/app_dependencies.tsx +++ b/x-pack/plugins/transform/public/app/app_dependencies.tsx @@ -10,6 +10,7 @@ import type { DataPublicPluginStart } from 'src/plugins/data/public'; import type { SavedObjectsStart } from 'src/plugins/saved_objects/public'; import type { ScopedHistory } from 'kibana/public'; import type { SharePluginStart } from 'src/plugins/share/public'; +import type { SpacesPluginStart } from '../../../spaces/public'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import type { Storage } from '../../../../../src/plugins/kibana_utils/public'; @@ -32,6 +33,7 @@ export interface AppDependencies { savedObjectsPlugin: SavedObjectsStart; share: SharePluginStart; ml: GetMlSharedImportsReturnType; + spaces?: SpacesPluginStart; } export const useAppDependencies = () => { diff --git a/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts b/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts index 3e85f5d4d49a4..db5fd82808a91 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts @@ -20,12 +20,10 @@ import { isIndexPattern } from '../../../../common/types/index_pattern'; export type SavedSearchQuery = object; type IndexPatternId = string; -type SavedSearchId = string; let indexPatternCache: Array>> = []; let fullIndexPatterns; let currentIndexPattern = null; -let currentSavedSearch = null; export let refreshIndexPatterns: () => Promise; @@ -76,11 +74,6 @@ export function loadCurrentIndexPattern( return currentIndexPattern; } -export function loadCurrentSavedSearch(savedSearches: any, savedSearchId: SavedSearchId) { - currentSavedSearch = savedSearches.get(savedSearchId); - return currentSavedSearch; -} - export interface SearchItems { indexPattern: IndexPattern; savedSearch: any; diff --git a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts index 44e3cc347824d..bd225ae9d0a32 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { isIndexPattern } from '../../../../common/types/index_pattern'; -import { createSavedSearchesLoader } from '../../../shared_imports'; +import { getSavedSearch, getSavedSearchUrlConflictMessage } from '../../../shared_imports'; import { useAppDependencies } from '../../app_dependencies'; @@ -20,7 +20,6 @@ import { getIndexPatternIdByTitle, loadCurrentIndexPattern, loadIndexPatterns, - loadCurrentSavedSearch, SearchItems, } from './common'; @@ -32,10 +31,6 @@ export const useSearchItems = (defaultSavedObjectId: string | undefined) => { const indexPatterns = appDeps.data.indexPatterns; const uiSettings = appDeps.uiSettings; const savedObjectsClient = appDeps.savedObjects.client; - const savedSearches = createSavedSearchesLoader({ - savedObjectsClient, - savedObjects: appDeps.savedObjectsPlugin, - }); const [searchItems, setSearchItems] = useState(undefined); @@ -52,7 +47,16 @@ export const useSearchItems = (defaultSavedObjectId: string | undefined) => { } try { - fetchedSavedSearch = await loadCurrentSavedSearch(savedSearches, id); + fetchedSavedSearch = await getSavedSearch(id, { + search: appDeps.data.search, + savedObjectsClient: appDeps.savedObjects.client, + spaces: appDeps.spaces, + }); + + if (fetchedSavedSearch?.sharingSavedObjectProps?.errorJSON) { + setError(await getSavedSearchUrlConflictMessage(fetchedSavedSearch)); + return; + } } catch (e) { // Just let fetchedSavedSearch stay undefined in case it doesn't exist. } diff --git a/x-pack/plugins/transform/public/app/mount_management_section.ts b/x-pack/plugins/transform/public/app/mount_management_section.ts index 1d39d233f8284..1747330818547 100644 --- a/x-pack/plugins/transform/public/app/mount_management_section.ts +++ b/x-pack/plugins/transform/public/app/mount_management_section.ts @@ -29,7 +29,7 @@ export async function mountManagementSection( const startServices = await getStartServices(); const [core, plugins] = startServices; const { application, chrome, docLinks, i18n, overlays, savedObjects, uiSettings } = core; - const { data, share } = plugins; + const { data, share, spaces } = plugins; const { docTitle } = chrome; // Initialize services @@ -53,6 +53,7 @@ export async function mountManagementSection( history, savedObjectsPlugin: plugins.savedObjects, share, + spaces, ml: await getMlSharedImports(), }; diff --git a/x-pack/plugins/transform/public/plugin.ts b/x-pack/plugins/transform/public/plugin.ts index 4ed4e64070344..da280452c1f0f 100644 --- a/x-pack/plugins/transform/public/plugin.ts +++ b/x-pack/plugins/transform/public/plugin.ts @@ -13,6 +13,7 @@ import type { HomePublicPluginSetup } from 'src/plugins/home/public'; import type { SavedObjectsStart } from 'src/plugins/saved_objects/public'; import type { ManagementSetup } from 'src/plugins/management/public'; import type { SharePluginStart } from 'src/plugins/share/public'; +import type { SpacesApi } from '../../spaces/public'; import { registerFeature } from './register_feature'; import type { PluginSetupContract as AlertingSetup } from '../../alerting/public'; import type { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; @@ -24,6 +25,7 @@ export interface PluginsDependencies { home: HomePublicPluginSetup; savedObjects: SavedObjectsStart; share: SharePluginStart; + spaces?: SpacesApi; alerting?: AlertingSetup; triggersActionsUi?: TriggersAndActionsUIPublicPluginSetup; } diff --git a/x-pack/plugins/transform/public/shared_imports.ts b/x-pack/plugins/transform/public/shared_imports.ts index edd27fd43c2af..b8f5d88205858 100644 --- a/x-pack/plugins/transform/public/shared_imports.ts +++ b/x-pack/plugins/transform/public/shared_imports.ts @@ -5,9 +5,12 @@ * 2.0. */ -export { createSavedSearchesLoader } from '../../../../src/plugins/discover/public'; export { XJsonMode } from '@kbn/ace'; export { UseRequestConfig, useRequest } from '../../../../src/plugins/es_ui_shared/public'; +export { + getSavedSearch, + getSavedSearchUrlConflictMessage, +} from '../../../../src/plugins/discover/public'; export { getMlSharedImports, From 5fdbd26229d9dfe69885f31c47fc6751c8bc3ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Wed, 13 Oct 2021 10:33:33 +0200 Subject: [PATCH 007/117] [APM] Fix typo in linting docs (#114764) --- x-pack/plugins/apm/dev_docs/linting.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/dev_docs/linting.md b/x-pack/plugins/apm/dev_docs/linting.md index a4fd3094f121c..7db7053e59061 100644 --- a/x-pack/plugins/apm/dev_docs/linting.md +++ b/x-pack/plugins/apm/dev_docs/linting.md @@ -1,6 +1,6 @@ -## Linting +# Linting -_Note: Run the following commands from the root of Kibana._ +_Note: Run the commands from the root of Kibana._ ### Typescript @@ -19,4 +19,3 @@ yarn prettier "./x-pack/plugins/apm/**/*.{tsx,ts,js}" --write ``` node scripts/eslint.js x-pack/legacy/plugins/apm ``` -diff --git a/x-pack/plugins/apm/dev_docs/feature_flags.md b/x-pack/plugins/apm/dev_docs/feature_flags.md From 802fb0252d85bb5275376125e1db0b304037533b Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Wed, 13 Oct 2021 11:35:15 +0200 Subject: [PATCH 008/117] [APM] Filter on tx metrics for instance stats (#114758) --- .../get_service_instances_transaction_statistics.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts index a51f4c4e0fb7d..089282d6f1c34 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts @@ -17,6 +17,7 @@ import { Coordinate } from '../../../../typings/timeseries'; import { kqlQuery, rangeQuery } from '../../../../../observability/server'; import { environmentQuery } from '../../../../common/utils/environment_query'; import { + getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, getTransactionDurationFieldForAggregatedTransactions, } from '../../helpers/aggregated_transactions'; @@ -111,6 +112,9 @@ export async function getServiceInstancesTransactionStatistics< ...rangeQuery(start, end), ...environmentQuery(environment), ...kqlQuery(kuery), + ...getDocumentTypeFilterForAggregatedTransactions( + searchAggregatedTransactions + ), ...(isComparisonSearch && serviceNodeIds ? [{ terms: { [SERVICE_NODE_NAME]: serviceNodeIds } }] : []), From db4bcdee2c3f2cea21ca2f623dfc3a9b7c7c8909 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Wed, 13 Oct 2021 04:48:20 -0500 Subject: [PATCH 009/117] [fleet] Tweak Header UI (#114704) --- ...-plugin-core-public.doclinksstart.links.md | 1 + ...kibana-plugin-core-public.doclinksstart.md | 2 +- .../public/doc_links/doc_links_service.ts | 3 ++ src/core/public/public.api.md | 1 + .../integrations/layouts/default.tsx | 50 ++++++++++++------- .../fleet/storybook/context/doc_links.ts | 2 + .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 8 files changed, 41 insertions(+), 20 deletions(-) diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 5871d7df6a7c5..055cf213dea90 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -9,6 +9,7 @@ ```typescript readonly links: { readonly settings: string; + readonly elasticStackGetStarted: string; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index a4e842c317256..51101c8804680 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,4 +17,4 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | \ No newline at end of file diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index a4ca5722c6aca..6039a0766a1a1 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -26,12 +26,14 @@ export class DocLinksService { const PLUGIN_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/plugins/${DOC_LINK_VERSION}/`; const APM_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/apm/`; const SECURITY_SOLUTION_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/`; + const STACK_GETTING_STARTED = `${ELASTIC_WEBSITE_URL}guide/en/elastic-stack-get-started/${DOC_LINK_VERSION}/`; return deepFreeze({ DOC_LINK_VERSION, ELASTIC_WEBSITE_URL, links: { settings: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/settings.html`, + elasticStackGetStarted: `${STACK_GETTING_STARTED}get-started-elastic-stack.html`, apm: { kibanaSettings: `${KIBANA_DOCS}apm-settings-in-kibana.html`, supportedServiceMaps: `${KIBANA_DOCS}service-maps.html#service-maps-supported`, @@ -508,6 +510,7 @@ export interface DocLinksStart { readonly ELASTIC_WEBSITE_URL: string; readonly links: { readonly settings: string; + readonly elasticStackGetStarted: string; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 324066764768d..7b0ec39d4a4d9 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -478,6 +478,7 @@ export interface DocLinksStart { // (undocumented) readonly links: { readonly settings: string; + readonly elasticStackGetStarted: string; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; diff --git a/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx b/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx index 70e55c9bd56b0..8ced172e696aa 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -17,7 +17,7 @@ import type { EuiTheme } from 'src/plugins/kibana_react/common'; import { useLink } from '../../../hooks'; import type { Section } from '../sections'; -import { useLinks } from '../hooks'; +import { useLinks, useStartServices } from '../hooks'; import { WithHeaderLayout } from './'; @@ -27,10 +27,14 @@ interface Props { } const Illustration = styled(EuiImage)` - margin-bottom: -68px; + margin-bottom: -77px; position: relative; - top: -20px; - width: 80%; + top: -16px; + width: 395px; +`; + +const Hero = styled.div` + text-align: right; `; const HeroImage = memo(() => { @@ -39,21 +43,24 @@ const HeroImage = memo(() => { const IS_DARK_THEME = theme.darkMode; return ( - + + + ); }); export const DefaultLayout: React.FunctionComponent = memo(({ section, children }) => { const { getHref } = useLink(); + const { docLinks } = useStartServices(); return ( = memo(({ section, ch

@@ -76,7 +83,16 @@ export const DefaultLayout: React.FunctionComponent = memo(({ section, ch

+ {i18n.translate('xpack.fleet.epm.pageSubtitleLinkText', { + defaultMessage: 'Getting started with Elastic Stack', + })} + + ), + }} />

diff --git a/x-pack/plugins/fleet/storybook/context/doc_links.ts b/x-pack/plugins/fleet/storybook/context/doc_links.ts index 56287dd9116a9..9b86ea03549f3 100644 --- a/x-pack/plugins/fleet/storybook/context/doc_links.ts +++ b/x-pack/plugins/fleet/storybook/context/doc_links.ts @@ -10,6 +10,8 @@ import type { DocLinksStart } from 'kibana/public'; export const getDocLinks = () => { const docLinks: DocLinksStart = { links: { + elasticStackGetStarted: + 'https://www.elastic.co/guide/en/elastic-stack-get-started/current/get-started-elastic-stack.html', fleet: { learnMoreBlog: 'https://www.elastic.co/blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 84d87cb020fe9..2c828513e9d33 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10743,7 +10743,6 @@ "xpack.fleet.epm.packageDetailsNav.packageCustomLinkText": "高度な設定", "xpack.fleet.epm.packageDetailsNav.packagePoliciesLinkText": "ポリシー", "xpack.fleet.epm.packageDetailsNav.settingsLinkText": "設定", - "xpack.fleet.epm.pageSubtitle": "Elasticエージェントを使用して一般的なアプリやサービスからデータを収集します", "xpack.fleet.epm.releaseBadge.betaDescription": "この統合は本番環境用ではありません。", "xpack.fleet.epm.releaseBadge.betaLabel": "ベータ", "xpack.fleet.epm.releaseBadge.experimentalDescription": "この統合は、急に変更されたり、将来のリリースで削除されたりする可能性があります。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index fa78b2eae1432..c0d3975461c7b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10857,7 +10857,6 @@ "xpack.fleet.epm.packageDetailsNav.packageCustomLinkText": "高级", "xpack.fleet.epm.packageDetailsNav.packagePoliciesLinkText": "策略", "xpack.fleet.epm.packageDetailsNav.settingsLinkText": "设置", - "xpack.fleet.epm.pageSubtitle": "使用 Elastic 代理从热门应用和服务中收集数据", "xpack.fleet.epm.releaseBadge.betaDescription": "在生产环境中不推荐使用此集成。", "xpack.fleet.epm.releaseBadge.betaLabel": "公测版", "xpack.fleet.epm.releaseBadge.experimentalDescription": "此集成可能有重大更改或将在未来版本中移除。", From 0bf0b94ee02a8735a639d27ad61f57948dd0d08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Wed, 13 Oct 2021 12:03:13 +0200 Subject: [PATCH 010/117] [Security Solution] [Endpoint] Event filters uses the new card design (#114126) * Adds new card design to event filters and also adds comments list * Adds nested comments * Hides comments if there are no commentes * Fixes i18n check error because duplicated key * Fix wrong type and unit test * Fixes ts error * Address pr comments and fix unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../artifact_entry_card.test.tsx | 22 +++- .../artifact_entry_card.tsx | 35 ++++-- .../artifact_entry_collapsible_card.tsx | 4 +- .../components/card_comments.tsx | 68 ++++++++++ .../components/criteria_conditions.tsx | 57 ++++++++- .../components/translations.ts | 12 ++ .../artifact_entry_card/test_utils.ts | 9 ++ .../components/artifact_entry_card/types.ts | 16 +-- .../utils/get_formatted_comments.tsx | 34 +++++ .../utils/map_to_artifact_info.ts | 1 + .../view/event_filters_list_page.test.tsx | 29 ++--- .../view/event_filters_list_page.tsx | 118 ++++++++++++------ .../components/trusted_apps_grid/index.tsx | 1 + 13 files changed, 329 insertions(+), 77 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_comments.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.tsx diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.test.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.test.tsx index 52299679ec87b..bde1961dd782d 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.test.tsx @@ -25,7 +25,7 @@ describe.each([ ) => ReturnType; beforeEach(() => { - item = generateItem(); + item = generateItem() as AnyArtifact; appTestContext = createAppRootMockRenderer(); render = (props = {}) => { renderResult = appTestContext.render( @@ -77,13 +77,31 @@ describe.each([ expect(renderResult.getByTestId('testCard-description').textContent).toEqual(item.description); }); + it("shouldn't display description", async () => { + render({ hideDescription: true }); + expect(renderResult.queryByTestId('testCard-description')).toBeNull(); + }); + it('should display default empty value if description does not exist', async () => { item.description = undefined; render(); - expect(renderResult.getByTestId('testCard-description').textContent).toEqual('—'); }); + it('should display comments if one exists', async () => { + render(); + if (isTrustedApp(item)) { + expect(renderResult.queryByTestId('testCard-comments')).toBeNull(); + } else { + expect(renderResult.queryByTestId('testCard-comments')).not.toBeNull(); + } + }); + + it("shouldn't display comments", async () => { + render({ hideComments: true }); + expect(renderResult.queryByTestId('testCard-comments')).toBeNull(); + }); + it('should display OS and criteria conditions', () => { render(); diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.tsx index d9709c64e092d..bee9a63c9cf69 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.tsx @@ -16,10 +16,11 @@ import { useNormalizedArtifact } from './hooks/use_normalized_artifact'; import { useTestIdGenerator } from '../hooks/use_test_id_generator'; import { CardContainerPanel } from './components/card_container_panel'; import { CardSectionPanel } from './components/card_section_panel'; +import { CardComments } from './components/card_comments'; import { usePolicyNavLinks } from './hooks/use_policy_nav_links'; import { MaybeImmutable } from '../../../../common/endpoint/types'; -export interface ArtifactEntryCardProps extends CommonProps { +export interface CommonArtifactEntryCardProps extends CommonProps { item: MaybeImmutable; /** * The list of actions for the card. Will display an icon with the actions in a menu if defined. @@ -34,12 +35,27 @@ export interface ArtifactEntryCardProps extends CommonProps { policies?: MenuItemPropsByPolicyId; } +export interface ArtifactEntryCardProps extends CommonArtifactEntryCardProps { + // A flag to hide description section, false by default + hideDescription?: boolean; + // A flag to hide comments section, false by default + hideComments?: boolean; +} + /** * Display Artifact Items (ex. Trusted App, Event Filter, etc) as a card. * This component is a TS Generic that allows you to set what the Item type is */ export const ArtifactEntryCard = memo( - ({ item, policies, actions, 'data-test-subj': dataTestSubj, ...commonProps }) => { + ({ + item, + policies, + actions, + hideDescription = false, + hideComments = false, + 'data-test-subj': dataTestSubj, + ...commonProps + }) => { const artifact = useNormalizedArtifact(item as AnyArtifact); const getTestId = useTestIdGenerator(dataTestSubj); const policyNavLinks = usePolicyNavLinks(artifact, policies); @@ -63,11 +79,16 @@ export const ArtifactEntryCard = memo( - -

- {artifact.description || getEmptyValue()} -

-
+ {!hideDescription ? ( + +

+ {artifact.description || getEmptyValue()} +

+
+ ) : null} + {!hideComments ? ( + + ) : null} diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_collapsible_card.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_collapsible_card.tsx index 43572ea234d31..673504c18a9df 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_collapsible_card.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_collapsible_card.tsx @@ -7,7 +7,7 @@ import React, { memo } from 'react'; import { EuiHorizontalRule } from '@elastic/eui'; -import { ArtifactEntryCardProps } from './artifact_entry_card'; +import { CommonArtifactEntryCardProps } from './artifact_entry_card'; import { CardContainerPanel } from './components/card_container_panel'; import { useNormalizedArtifact } from './hooks/use_normalized_artifact'; import { useTestIdGenerator } from '../hooks/use_test_id_generator'; @@ -15,7 +15,7 @@ import { CardSectionPanel } from './components/card_section_panel'; import { CriteriaConditions, CriteriaConditionsProps } from './components/criteria_conditions'; import { CardCompressedHeader } from './components/card_compressed_header'; -export interface ArtifactEntryCollapsibleCardProps extends ArtifactEntryCardProps { +export interface ArtifactEntryCollapsibleCardProps extends CommonArtifactEntryCardProps { onExpandCollapse: () => void; expanded?: boolean; } diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_comments.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_comments.tsx new file mode 100644 index 0000000000000..ce539554a722e --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_comments.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo, useCallback, useState } from 'react'; +import { + CommonProps, + EuiAccordion, + EuiCommentList, + EuiCommentProps, + EuiButtonEmpty, + EuiSpacer, +} from '@elastic/eui'; +import { isEmpty } from 'lodash/fp'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; +import { CardActionsFlexItemProps } from './card_actions_flex_item'; +import { ArtifactInfo } from '../types'; +import { getFormattedComments } from '../utils/get_formatted_comments'; +import { SHOW_COMMENTS_LABEL, HIDE_COMMENTS_LABEL } from './translations'; + +export interface CardCommentsProps + extends CardActionsFlexItemProps, + Pick { + comments: ArtifactInfo['comments']; +} + +export const CardComments = memo( + ({ comments, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + const [showComments, setShowComments] = useState(false); + const onCommentsClick = useCallback((): void => { + setShowComments(!showComments); + }, [setShowComments, showComments]); + const formattedComments = useMemo((): EuiCommentProps[] => { + return getFormattedComments(comments); + }, [comments]); + + const buttonText = useMemo( + () => + showComments ? HIDE_COMMENTS_LABEL(comments.length) : SHOW_COMMENTS_LABEL(comments.length), + [comments.length, showComments] + ); + + return !isEmpty(comments) ? ( +
+ + + {buttonText} + + + + + +
+ ) : null; + } +); + +CardComments.displayName = 'CardComments'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx index 292c06c4f5b8c..260db313ced36 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import React, { memo } from 'react'; -import { CommonProps, EuiExpression } from '@elastic/eui'; +import React, { memo, useCallback } from 'react'; +import { CommonProps, EuiExpression, EuiToken, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { CONDITION_OS, @@ -21,7 +22,7 @@ import { CONDITION_OPERATOR_TYPE_EXISTS, CONDITION_OPERATOR_TYPE_LIST, } from './translations'; -import { ArtifactInfo } from '../types'; +import { ArtifactInfo, ArtifactInfoEntry } from '../types'; import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; const OS_LABELS = Object.freeze({ @@ -39,6 +40,15 @@ const OPERATOR_TYPE_LABELS = Object.freeze({ [ListOperatorTypeEnum.LIST]: CONDITION_OPERATOR_TYPE_LIST, }); +const EuiFlexGroupNested = styled(EuiFlexGroup)` + margin-left: ${({ theme }) => theme.eui.spacerSizes.l}; +`; + +const EuiFlexItemNested = styled(EuiFlexItem)` + margin-bottom: 6px !important; + margin-top: 6px !important; +`; + export type CriteriaConditionsProps = Pick & Pick; @@ -46,6 +56,44 @@ export const CriteriaConditions = memo( ({ os, entries, 'data-test-subj': dataTestSubj }) => { const getTestId = useTestIdGenerator(dataTestSubj); + const getNestedEntriesContent = useCallback( + (type: string, nestedEntries: ArtifactInfoEntry[]) => { + if (type === 'nested' && nestedEntries.length) { + return nestedEntries.map( + ({ field: nestedField, type: nestedType, value: nestedValue }) => { + return ( + + + + + + + + + + + + ); + } + ); + } + }, + [getTestId] + ); + return (
@@ -57,7 +105,7 @@ export const CriteriaConditions = memo( />
- {entries.map(({ field, type, value }) => { + {entries.map(({ field, type, value, entries: nestedEntries = [] }) => { return (
@@ -67,6 +115,7 @@ export const CriteriaConditions = memo( } value={value} /> + {getNestedEntriesContent(type, nestedEntries)}
); })} diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts index d98f4589027d4..512724b66d50e 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts @@ -114,3 +114,15 @@ export const COLLAPSE_ACTION = i18n.translate( defaultMessage: 'Collapse', } ); + +export const SHOW_COMMENTS_LABEL = (count: number = 0) => + i18n.translate('xpack.securitySolution.artifactCard.comments.label.show', { + defaultMessage: 'Show comments ({count})', + values: { count }, + }); + +export const HIDE_COMMENTS_LABEL = (count: number = 0) => + i18n.translate('xpack.securitySolution.artifactCard.comments.label.hide', { + defaultMessage: 'Hide comments ({count})', + values: { count }, + }); diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/test_utils.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/test_utils.ts index d9ff23b3a2c6a..d7d8899b005ed 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/test_utils.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/test_utils.ts @@ -6,6 +6,7 @@ */ import { cloneDeep } from 'lodash'; +import uuid from 'uuid'; import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { TrustedAppGenerator } from '../../../../common/endpoint/data_generators/trusted_app_generator'; import { getExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; @@ -54,6 +55,14 @@ export const getExceptionProviderMock = (): ExceptionListItemSchema => { }, ], tags: ['policy:all'], + comments: [ + { + id: uuid.v4(), + comment: 'test', + created_at: new Date().toISOString(), + created_by: 'Justa', + }, + ], }) ); }; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/types.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/types.ts index c506c62ac4351..fe50a15190f11 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/types.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/types.ts @@ -10,6 +10,13 @@ import { EffectScope, TrustedApp } from '../../../../common/endpoint/types'; import { ContextMenuItemNavByRouterProps } from '../context_menu_with_router_support/context_menu_item_nav_by_router'; export type AnyArtifact = ExceptionListItemSchema | TrustedApp; +export interface ArtifactInfoEntry { + field: string; + type: string; + operator: string; + value: string; +} +type ArtifactInfoEntries = ArtifactInfoEntry & { entries?: ArtifactInfoEntry[] }; /** * A normalized structured that is used internally through out the card's components. @@ -17,16 +24,11 @@ export type AnyArtifact = ExceptionListItemSchema | TrustedApp; export interface ArtifactInfo extends Pick< ExceptionListItemSchema, - 'name' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by' | 'description' + 'name' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by' | 'description' | 'comments' > { effectScope: EffectScope; os: string; - entries: Array<{ - field: string; - type: string; - operator: string; - value: string; - }>; + entries: ArtifactInfoEntries[]; } export interface MenuItemPropsByPolicyId { diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.tsx new file mode 100644 index 0000000000000..c9e96c5ce9ec1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.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 from 'react'; +import { EuiAvatar, EuiText, EuiCommentProps } from '@elastic/eui'; +import styled from 'styled-components'; +import { CommentsArray } from '@kbn/securitysolution-io-ts-list-types'; +import { COMMENT_EVENT } from '../../../../common/components/exceptions/translations'; +import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; + +const CustomEuiAvatar = styled(EuiAvatar)` + background-color: ${({ theme }) => theme.eui.euiColorLightShade} !important; +`; + +/** + * Formats ExceptionItem.comments into EuiCommentList format + * + * @param comments ExceptionItem.comments + */ +export const getFormattedComments = (comments: CommentsArray): EuiCommentProps[] => { + return comments.map((commentItem) => ({ + username: commentItem.created_by, + timestamp: ( + + ), + event: COMMENT_EVENT, + timelineIcon: , + children: {commentItem.comment}, + })); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/map_to_artifact_info.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/map_to_artifact_info.ts index dd9e90db327ee..5969cf9d043b4 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/map_to_artifact_info.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/map_to_artifact_info.ts @@ -24,6 +24,7 @@ export const mapToArtifactInfo = (_item: MaybeImmutable): ArtifactI updated_at, updated_by, description, + comments: isTrustedApp(item) ? [] : item.comments, entries: entries as unknown as ArtifactInfo['entries'], os: isTrustedApp(item) ? item.os : getOsFromExceptionItem(item), effectScope: isTrustedApp(item) ? item.effectScope : getEffectScopeFromExceptionItem(item), diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx index 37269108aa10f..0729f95bb44a9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx @@ -109,19 +109,15 @@ describe('When on the Event Filters List Page', () => { it('should render expected fields on card', async () => { render(); await dataReceived(); - const eventMeta = ([] as HTMLElement[]).map.call( - renderResult.getByTestId('exceptionsViewerItemDetails').querySelectorAll('dd'), - (ele) => ele.textContent - ); - expect(eventMeta).toEqual([ - 'some name', - 'April 20th 2020 @ 11:25:31', - 'some user', - 'April 20th 2020 @ 11:25:31', - 'some user', - 'some description', - ]); + [ + ['subHeader-touchedBy-createdBy-value', 'some user'], + ['subHeader-touchedBy-updatedBy-value', 'some user'], + ['header-created-value', '4/20/2020'], + ['header-updated-value', '4/20/2020'], + ].forEach(([suffix, value]) => + expect(renderResult.getByTestId(`eventFilterCard-${suffix}`).textContent).toEqual(value) + ); }); it('should show API error if one is encountered', async () => { @@ -143,8 +139,13 @@ describe('When on the Event Filters List Page', () => { it('should show modal when delete is clicked on a card', async () => { render(); await dataReceived(); - act(() => { - fireEvent.click(renderResult.getByTestId('exceptionsViewerDeleteBtn')); + + await act(async () => { + (await renderResult.findAllByTestId('eventFilterCard-header-actions-button'))[0].click(); + }); + + await act(async () => { + (await renderResult.findByTestId('deleteEventFilterAction')).click(); }); expect( diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx index e206f85df6548..db4e5dbb531b2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx @@ -36,16 +36,20 @@ import { } from '../store/selector'; import { PaginatedContent, PaginatedContentProps } from '../../../components/paginated_content'; import { Immutable, ListPageRouteState } from '../../../../../common/endpoint/types'; +import { ExceptionItem } from '../../../../common/components/exceptions/viewer/exception_item'; import { - ExceptionItem, - ExceptionItemProps, -} from '../../../../common/components/exceptions/viewer/exception_item'; + AnyArtifact, + ArtifactEntryCard, + ArtifactEntryCardProps, +} from '../../../components/artifact_entry_card'; import { EventFilterDeleteModal } from './components/event_filter_delete_modal'; import { SearchExceptions } from '../../../components/search_exceptions'; import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; import { ABOUT_EVENT_FILTERS } from './translations'; +type ArtifactEntryCardType = typeof ArtifactEntryCard; + type EventListPaginatedContent = PaginatedContentProps< Immutable, typeof ExceptionItem @@ -61,6 +65,20 @@ const AdministrationListPage = styled(_AdministrationListPage)` } `; +const EDIT_EVENT_FILTER_ACTION_LABEL = i18n.translate( + 'xpack.securitySolution.eventFilters.list.cardAction.edit', + { + defaultMessage: 'Edit event filter', + } +); + +const DELETE_EVENT_FILTER_ACTION_LABEL = i18n.translate( + 'xpack.securitySolution.eventFilters.list.cardAction.delete', + { + defaultMessage: 'Delete event filter', + } +); + export const EventFiltersListPage = memo(() => { const { state: routeState } = useLocation(); const history = useHistory(); @@ -133,41 +151,6 @@ export const EventFiltersListPage = memo(() => { [navigateCallback] ); - const handleItemEdit: ExceptionItemProps['onEditException'] = useCallback( - (item: ExceptionListItemSchema) => { - navigateCallback({ - show: 'edit', - id: item.id, - }); - }, - [navigateCallback] - ); - - const handleItemDelete: ExceptionItemProps['onDeleteException'] = useCallback( - ({ id }) => { - dispatch({ - type: 'eventFilterForDeletion', - // Casting below needed due to error around the comments array needing to be mutable - payload: listItems.find((item) => item.id === id)! as ExceptionListItemSchema, - }); - }, - [dispatch, listItems] - ); - - const handleItemComponentProps: EventListPaginatedContent['itemComponentProps'] = useCallback( - (exceptionItem) => ({ - exceptionItem: exceptionItem as ExceptionListItemSchema, - loadingItemIds: [], - commentsAccordionId: '', - onEditException: handleItemEdit, - onDeleteException: handleItemDelete, - showModified: true, - showName: true, - 'data-test-subj': `eventFilterCard`, - }), - [handleItemDelete, handleItemEdit] - ); - const handlePaginatedContentChange: EventListPaginatedContent['onChange'] = useCallback( ({ pageIndex, pageSize }) => { navigateCallback({ @@ -186,6 +169,59 @@ export const EventFiltersListPage = memo(() => { [navigateCallback, dispatch] ); + const artifactCardPropsPerItem = useMemo(() => { + const cachedCardProps: Record = {}; + + // Casting `listItems` below to remove the `Immutable<>` from it in order to prevent errors + // with common component's props + for (const eventFilter of listItems as ExceptionListItemSchema[]) { + let policies: ArtifactEntryCardProps['policies']; + + cachedCardProps[eventFilter.id] = { + item: eventFilter as AnyArtifact, + policies, + hideDescription: true, + 'data-test-subj': 'eventFilterCard', + actions: [ + { + icon: 'controlsHorizontal', + onClick: () => { + history.push( + getEventFiltersListPath({ + ...location, + show: 'edit', + id: eventFilter.id, + }) + ); + }, + 'data-test-subj': 'editEventFilterAction', + children: EDIT_EVENT_FILTER_ACTION_LABEL, + }, + { + icon: 'trash', + onClick: () => { + dispatch({ + type: 'eventFilterForDeletion', + payload: eventFilter, + }); + }, + 'data-test-subj': 'deleteEventFilterAction', + children: DELETE_EVENT_FILTER_ACTION_LABEL, + }, + ], + }; + } + + return cachedCardProps; + }, [dispatch, history, listItems, location]); + + const handleArtifactCardProps = useCallback( + (eventFilter: ExceptionListItemSchema) => { + return artifactCardPropsPerItem[eventFilter.id]; + }, + [artifactCardPropsPerItem] + ); + return ( { )} - , typeof ExceptionItem> + items={listItems} - ItemComponent={ExceptionItem} - itemComponentProps={handleItemComponentProps} + ItemComponent={ArtifactEntryCard} + itemComponentProps={handleArtifactCardProps} onChange={handlePaginatedContentChange} error={fetchError?.message} loading={isLoading} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx index ba09d0c7ee0cc..948b454f9cc00 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx @@ -138,6 +138,7 @@ export const TrustedAppsGrid = memo(() => { cachedCardProps[trustedApp.id] = { item: trustedApp, policies, + hideComments: true, 'data-test-subj': 'trustedAppCard', actions: [ { From 708f06fc153ff9c92bfe54bd2be5b9b233a80151 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 13 Oct 2021 07:15:26 -0500 Subject: [PATCH 011/117] [data views] add getDefaultDataView method (#113891) * add new method to data views api * add tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/data_views/data_views.test.ts | 58 +++++++++++++++++-- .../common/data_views/data_views.ts | 31 ++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) 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 9a01e52ce48e2..210c926f92df7 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 @@ -50,9 +50,15 @@ describe('IndexPatterns', () => { let indexPatterns: DataViewsService; let savedObjectsClient: SavedObjectsClientCommon; let SOClientGetDelay = 0; + const uiSettings = { + get: () => Promise.resolve(false), + getAll: () => {}, + set: () => () => {}, + remove: jest.fn(), + } as any as UiSettingsCommon; + const indexPatternObj = { id: 'id', version: 'a', attributes: { title: 'title' } }; beforeEach(() => { - const indexPatternObj = { id: 'id', version: 'a', attributes: { title: 'title' } }; savedObjectsClient = {} as SavedObjectsClientCommon; savedObjectsClient.find = jest.fn( () => Promise.resolve([indexPatternObj]) as Promise>> @@ -86,10 +92,7 @@ describe('IndexPatterns', () => { }); indexPatterns = new DataViewsService({ - uiSettings: { - get: () => Promise.resolve(false), - getAll: () => {}, - } as any as UiSettingsCommon, + uiSettings, savedObjectsClient: savedObjectsClient as unknown as SavedObjectsClientCommon, apiClient: createFieldsFetcher(), fieldFormats, @@ -274,4 +277,49 @@ describe('IndexPatterns', () => { // successful subsequent request expect(async () => await indexPatterns.get(id)).toBeDefined(); }); + + describe('getDefaultDataView', () => { + test('gets default data view', async () => { + indexPatterns.clearCache(); + jest.clearAllMocks(); + + expect(await indexPatterns.getDefaultDataView()).toBeInstanceOf(DataView); + // make sure we're not pulling from cache + expect(savedObjectsClient.get).toBeCalledTimes(1); + expect(savedObjectsClient.find).toBeCalledTimes(1); + }); + + test('returns undefined if no data views exist', async () => { + savedObjectsClient.find = jest.fn( + () => Promise.resolve([]) as Promise>> + ); + savedObjectsClient.get = jest.fn(() => Promise.resolve(undefined) as Promise); + indexPatterns.clearCache(); + expect(await indexPatterns.getDefaultDataView()).toBeUndefined(); + }); + + test("default doesn't exist, grabs another data view", async () => { + indexPatterns.clearCache(); + jest.clearAllMocks(); + uiSettings.get = jest.fn().mockResolvedValue(['bar']); + + savedObjectsClient.find = jest.fn( + () => Promise.resolve([indexPatternObj]) as Promise>> + ); + + savedObjectsClient.get = jest.fn().mockResolvedValue({ + id: 'bar', + version: 'foo', + attributes: { + title: 'something', + }, + }); + + expect(await indexPatterns.getDefaultDataView()).toBeInstanceOf(DataView); + // make sure we're not pulling from cache + expect(savedObjectsClient.get).toBeCalledTimes(1); + expect(savedObjectsClient.find).toBeCalledTimes(1); + expect(uiSettings.remove).toBeCalledTimes(1); + }); + }); }); 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 77ce1caaaad84..a76b531668162 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -79,6 +79,10 @@ export class DataViewsService { private onError: OnError; private dataViewCache: ReturnType; + /** + * @deprecated Use `getDefaultDataView` instead (when loading data view) and handle + * 'no data view' case in api consumer code - no more auto redirect + */ ensureDefaultDataView: EnsureDefaultDataView; constructor({ @@ -681,6 +685,33 @@ export class DataViewsService { this.dataViewCache.clear(indexPatternId); return this.savedObjectsClient.delete(DATA_VIEW_SAVED_OBJECT_TYPE, indexPatternId); } + + /** + * Returns the default data view as an object. If no default is found, or it is missing + * another data view is selected as default and returned. + * @returns default data view + */ + + async getDefaultDataView() { + const patterns = await this.getIds(); + let defaultId = await this.config.get('defaultIndex'); + let defined = !!defaultId; + const exists = patterns.includes(defaultId); + + if (defined && !exists) { + await this.config.remove('defaultIndex'); + defaultId = defined = false; + } + + if (patterns.length >= 1 && (await this.hasUserDataView().catch(() => true))) { + defaultId = patterns[0]; + await this.config.set('defaultIndex', defaultId); + } + + if (defaultId) { + return this.get(defaultId); + } + } } /** From 2afb43b8699d91463be52f57e78d277fbd319471 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 13 Oct 2021 14:52:48 +0200 Subject: [PATCH 012/117] Replace EuiCodeEditor with CodeEditor in app-services code (#114316) * code editor scripted fields * filter_editor * embeddable example * clean * update tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dashboard_embeddable_examples/kibana.json | 3 +- .../public/app.tsx | 32 ++++++++++++------- .../public/by_value/input_editor.tsx | 13 ++++---- .../public/plugin.tsx | 3 +- .../tsconfig.json | 2 +- .../filter_editor/filter_editor.test.tsx | 8 ++--- .../ui/filter_bar/filter_editor/index.tsx | 15 +++++---- .../__snapshots__/field_editor.test.tsx.snap | 28 ++++++++++------ .../field_editor/field_editor.test.tsx | 12 +++++-- .../components/field_editor/field_editor.tsx | 20 ++++++------ test/examples/embeddables/dashboard.ts | 14 ++------ test/functional/page_objects/settings_page.ts | 10 ++---- 12 files changed, 85 insertions(+), 75 deletions(-) diff --git a/examples/dashboard_embeddable_examples/kibana.json b/examples/dashboard_embeddable_examples/kibana.json index e13f19194ef06..ba0c4a84836e7 100644 --- a/examples/dashboard_embeddable_examples/kibana.json +++ b/examples/dashboard_embeddable_examples/kibana.json @@ -16,6 +16,5 @@ "githubTeam": "kibana-presentation" }, "description": "Example app that shows how to embed a dashboard in an application", - "optionalPlugins": [], - "requiredBundles": ["esUiShared"] + "optionalPlugins": [] } diff --git a/examples/dashboard_embeddable_examples/public/app.tsx b/examples/dashboard_embeddable_examples/public/app.tsx index 8a6b5a90a22a8..c44d67c49f3d7 100644 --- a/examples/dashboard_embeddable_examples/public/app.tsx +++ b/examples/dashboard_embeddable_examples/public/app.tsx @@ -18,9 +18,10 @@ import { EuiSideNav, } from '@elastic/eui'; import 'brace/mode/json'; -import { AppMountParameters } from '../../../src/core/public'; +import { AppMountParameters, IUiSettingsClient } from '../../../src/core/public'; import { DashboardEmbeddableByValue } from './by_value/embeddable'; import { DashboardStart } from '../../../src/plugins/dashboard/public'; +import { KibanaContextProvider } from '../../../src/plugins/kibana_react/public'; interface PageDef { title: string; @@ -58,9 +59,14 @@ interface Props { DashboardContainerByValueRenderer: ReturnType< DashboardStart['getDashboardContainerByValueRenderer'] >; + uiSettings: IUiSettingsClient; } -const DashboardEmbeddableExplorerApp = ({ basename, DashboardContainerByValueRenderer }: Props) => { +const DashboardEmbeddableExplorerApp = ({ + basename, + DashboardContainerByValueRenderer, + uiSettings, +}: Props) => { const pages: PageDef[] = [ { title: 'By value dashboard embeddable', @@ -83,16 +89,18 @@ const DashboardEmbeddableExplorerApp = ({ basename, DashboardContainerByValueRen )); return ( - - - -