From 1d98cb6512d7699b2bf8b515a9e522d4508b0538 Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Wed, 18 Aug 2021 09:12:55 +0200 Subject: [PATCH 01/11] [Upgrade Assistant] Overview page redesign (#106521) --- .../public/doc_links/doc_links_service.ts | 2 + .../translations/translations/ja-JP.json | 17 -- .../translations/translations/zh-CN.json | 17 -- .../client_integration/cluster.test.ts | 5 +- .../helpers/http_requests.ts | 6 +- .../helpers/overview.helpers.ts | 25 +- .../helpers/services_mock.ts | 30 ++ .../helpers/setup_environment.tsx | 17 +- .../client_integration/indices.test.ts | 5 +- .../client_integration/overview.test.ts | 270 ------------------ .../fix_deprecation_logs_step.test.tsx | 153 ++++++++++ .../review_logs_step/mocked_responses.ts | 56 ++++ .../review_logs_step.test.tsx | 233 +++++++++++++++ .../upgrade_step/upgrade_step.test.tsx | 55 ++++ .../upgrade_assistant/common/constants.ts | 4 + .../plugins/upgrade_assistant/common/types.ts | 5 + x-pack/plugins/upgrade_assistant/kibana.json | 4 +- .../public/application/app.tsx | 33 ++- .../components/latest_minor_banner.tsx | 60 ---- .../components/overview/_index.scss | 3 +- .../components/overview/_steps.scss | 6 - .../overview/deprecation_logging_toggle.tsx | 201 ------------- .../components/overview/es_stats.tsx | 166 ----------- .../fix_deprecation_logs_step/_index.scss | 1 + .../_deprecation_logging_toggle.scss | 7 + .../deprecation_logging_toggle.tsx | 154 ++++++++++ .../deprecation_logging_toggle/index.ts | 8 + .../external_links.tsx | 120 ++++++++ .../fix_deprecation_logs_step.tsx | 90 ++++++ .../fix_deprecation_logs_step/index.ts | 8 + .../use_deprecation_logging.ts | 89 ++++++ .../application/components/overview/index.ts | 2 +- .../components/overview/kibana_stats.tsx | 194 ------------- .../components/overview/overview.tsx | 170 ++++------- .../overview/review_logs_step/_index.scss | 2 + .../review_logs_step/_stats_panel.scss | 6 + .../review_logs_step/es_stats/es_stats.tsx | 153 ++++++++++ .../es_stats}/es_stats_error.tsx | 23 +- .../review_logs_step/es_stats/index.ts | 8 + .../overview/review_logs_step/index.ts | 8 + .../review_logs_step/kibana_stats/index.ts | 8 + .../kibana_stats/kibana_stats.tsx | 188 ++++++++++++ .../no_deprecations/_no_deprecations.scss | 3 + .../review_logs_step/no_deprecations/index.ts | 8 + .../no_deprecations/no_deprecations.tsx | 35 +++ .../review_logs_step/review_logs_step.tsx | 53 ++++ .../components/overview/upgrade_step/index.ts | 8 + .../overview/upgrade_step/upgrade_step.tsx | 129 +++++++++ .../public/application/components/types.ts | 11 + .../public/application/lib/api.ts | 5 +- .../public/application/lib/utils.test.ts | 17 +- .../public/application/lib/utils.ts | 15 + .../application/mount_management_section.ts | 6 +- .../upgrade_assistant/public/plugin.ts | 18 +- .../public/shared_imports.ts | 9 + .../plugins/upgrade_assistant/public/types.ts | 28 ++ .../lib/es_deprecation_logging_apis.test.ts | 44 ++- .../server/lib/es_deprecation_logging_apis.ts | 31 +- .../lib/telemetry/usage_collector.test.ts | 5 + .../server/lib/telemetry/usage_collector.ts | 10 +- .../upgrade_assistant/server/plugin.ts | 22 +- .../server/routes/deprecation_logging.test.ts | 29 +- .../plugins/upgrade_assistant/tsconfig.json | 2 + 63 files changed, 1975 insertions(+), 1125 deletions(-) create mode 100644 x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/services_mock.ts delete mode 100644 x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview.test.ts create mode 100644 x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.test.tsx create mode 100644 x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts create mode 100644 x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx create mode 100644 x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx delete mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/latest_minor_banner.tsx delete mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/_steps.scss delete mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/deprecation_logging_toggle.tsx delete mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/es_stats.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/_index.scss create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/_deprecation_logging_toggle.scss create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/index.ts create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/external_links.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/index.ts create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/use_deprecation_logging.ts delete mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/kibana_stats.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_index.scss create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_stats_panel.scss create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx rename x-pack/plugins/upgrade_assistant/public/application/components/overview/{ => review_logs_step/es_stats}/es_stats_error.tsx (80%) create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/index.ts create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/index.ts create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/index.ts create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/kibana_stats.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/_no_deprecations.scss create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/index.ts create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/no_deprecations.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/review_logs_step.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/index.ts create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx create mode 100644 x-pack/plugins/upgrade_assistant/public/types.ts diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 1f6b549c0110c..88075b66ad045 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -198,6 +198,8 @@ export class DocLinksService { transportSettings: `${ELASTICSEARCH_DOCS}modules-network.html#common-network-settings`, typesRemoval: `${ELASTICSEARCH_DOCS}removal-of-types.html`, deprecationLogging: `${ELASTICSEARCH_DOCS}logging.html#deprecation-logging`, + setupUpgrade: `${ELASTICSEARCH_DOCS}setup-upgrade.html`, + releaseHighlights: `${ELASTICSEARCH_DOCS}release-highlights.html`, }, siem: { guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 68c8a957c3e52..68d4482afd92c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24797,7 +24797,6 @@ "xpack.upgradeAssistant.deprecationListSearchBar.placeholderAriaLabel": "フィルター", "xpack.upgradeAssistant.deprecationListSearchBar.placeholderLabel": "フィルター", "xpack.upgradeAssistant.deprecationListSearchBar.reloadButtonLabel": "再読み込み", - "xpack.upgradeAssistant.deprecationLoggingDescription.deprecationLoggingLink": "廃止予定ログ", "xpack.upgradeAssistant.emptyPrompt.learnMoreDescription": "{nextMajor}への移行に関する詳細をご覧ください。", "xpack.upgradeAssistant.emptyPrompt.title": "{uaVersion} アップグレードアシスタント", "xpack.upgradeAssistant.emptyPrompt.upgradeAssistantDescription": "アップグレードアシスタントはクラスターの廃止予定の設定を特定し、アップグレード前に問題を解決できるようにします。Elastic {nextMajor}にアップグレードするときにここに戻って確認してください。", @@ -24819,9 +24818,7 @@ "xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsTitle": "重大", "xpack.upgradeAssistant.esDeprecationStats.loadingText": "Elasticsearchの廃止統計情報を読み込んでいます...", "xpack.upgradeAssistant.esDeprecationStats.statsTitle": "Elasticsearch", - "xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTitle": "廃止予定", "xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTooltip": "このクラスターは{clusterCount}個の廃止予定のクラスター設定と{indexCount}個の廃止予定のインデックス設定を使用しています。", - "xpack.upgradeAssistant.esDeprecationStats.viewDeprecationsLinkText": "廃止予定を表示", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorDescription": "エラーについては、Kibanaサーバーログを確認してください。", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorTitle": "Kibana廃止予定を取得できませんでした", "xpack.upgradeAssistant.kibanaDeprecationErrors.pluginErrorDescription": "エラーについては、Kibanaサーバーログを確認してください。", @@ -24845,23 +24842,12 @@ "xpack.upgradeAssistant.kibanaDeprecationStats.loadingErrorMessage": "Kibana廃止予定の取得中にエラーが発生しました。", "xpack.upgradeAssistant.kibanaDeprecationStats.loadingText": "Kibana廃止予定統計情報を読み込んでいます…", "xpack.upgradeAssistant.kibanaDeprecationStats.statsTitle": "Kibana", - "xpack.upgradeAssistant.kibanaDeprecationStats.totalDeprecationsLabel": "Kibanaには合計{totalDeprecations}個の廃止予定があります", - "xpack.upgradeAssistant.kibanaDeprecationStats.totalDeprecationsTitle": "廃止予定", - "xpack.upgradeAssistant.kibanaDeprecationStats.viewDeprecationsLinkText": "廃止予定を表示", "xpack.upgradeAssistant.noDeprecationsPrompt.description": "構成は最新です。", "xpack.upgradeAssistant.noDeprecationsPrompt.nextStepsDescription": "他のスタック廃止予定については、{overviewButton}を確認してください。", "xpack.upgradeAssistant.noDeprecationsPrompt.overviewLinkText": "概要ページ", "xpack.upgradeAssistant.noDeprecationsPrompt.title": "アップグレードする準備ができました。", - "xpack.upgradeAssistant.overview.deprecationLogging.loadingLabel": "ロギング状態を取得しています", - "xpack.upgradeAssistant.overview.deprecationLoggingDescription": "{deprecationLoggingLink}を有効にすると、Elastic {nextMajor}にアップグレードした後に使用できない廃止予定の機能を使用しているかどうかがわかります。", - "xpack.upgradeAssistant.overview.deprecationLoggingTitle": "廃止ログ", - "xpack.upgradeAssistant.overview.deprecationLogs.disableButtonLabel": "廃止ログを無効にする", "xpack.upgradeAssistant.overview.deprecationLogs.disabledToastMessage": "廃止予定のアクションをログに出力しません。", - "xpack.upgradeAssistant.overview.deprecationLogs.disablingButtonLabel": "廃止ログを無効にしています", - "xpack.upgradeAssistant.overview.deprecationLogs.enableButtonLabel": "廃止ログを有効にする", "xpack.upgradeAssistant.overview.deprecationLogs.enabledToastMessage": "廃止予定のアクションをログに出力します。", - "xpack.upgradeAssistant.overview.deprecationLogs.enablingButtonLabel": "廃止ログを有効にしています", - "xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorButtonLabel": "廃止ログがありません", "xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorMessage": "ログ情報を取得できませんでした。", "xpack.upgradeAssistant.overview.deprecationLogs.reloadButtonLabel": "再試行", "xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage": "ログ状態を更新できませんでした。", @@ -24869,9 +24855,6 @@ "xpack.upgradeAssistant.overview.pageDescription": "廃止予定の設定を特定し、構成を更新して、アップグレードの準備を行います。", "xpack.upgradeAssistant.overview.pageTitle": "アップグレードアシスタント", "xpack.upgradeAssistant.reindex.reindexPrivilegesErrorBatch": "「{indexName}」に再インデックスするための権限が不十分です。", - "xpack.upgradeAssistant.tabs.incompleteCallout.calloutBody.breackingChangesDocButtonLabel": "廃止と互換性を破る変更", - "xpack.upgradeAssistant.tabs.incompleteCallout.calloutBody.calloutDetail": "Elasticsearch {nextEsVersion} の {breakingChangesDocButton} の完全なリストは、最終の {currentEsVersion} マイナーリリースで確認できます。この警告は、リストがすべて解決されると消えます。", - "xpack.upgradeAssistant.tabs.incompleteCallout.calloutTitle": "リストの問題がすべて解決されていない可能性があります。", "xpack.uptime.addDataButtonLabel": "データの追加", "xpack.uptime.alerts.anomaly.criteriaExpression.ariaLabel": "選択したモニターの条件を表示する式。", "xpack.uptime.alerts.anomaly.criteriaExpression.description": "監視するとき", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6b5b171de35b8..fc0613e8b46ea 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25350,7 +25350,6 @@ "xpack.upgradeAssistant.deprecationListSearchBar.placeholderAriaLabel": "筛选", "xpack.upgradeAssistant.deprecationListSearchBar.placeholderLabel": "筛选", "xpack.upgradeAssistant.deprecationListSearchBar.reloadButtonLabel": "重新加载", - "xpack.upgradeAssistant.deprecationLoggingDescription.deprecationLoggingLink": "弃用日志记录", "xpack.upgradeAssistant.emptyPrompt.learnMoreDescription": "详细了解如何迁移到 {nextMajor}。", "xpack.upgradeAssistant.emptyPrompt.title": "{uaVersion} 升级助手", "xpack.upgradeAssistant.emptyPrompt.upgradeAssistantDescription": "升级助手识别集群中弃用的设置,帮助您在升级前解决问题。需要升级到 Elastic {nextMajor} 时,回到这里查看。", @@ -25372,9 +25371,7 @@ "xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsTitle": "紧急", "xpack.upgradeAssistant.esDeprecationStats.loadingText": "正在加载 Elasticsearch 弃用统计……", "xpack.upgradeAssistant.esDeprecationStats.statsTitle": "Elasticsearch", - "xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTitle": "弃用", "xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTooltip": "此集群正在使用 {clusterCount} 个已弃用集群设置和 {indexCount} 个已弃用的索引设置", - "xpack.upgradeAssistant.esDeprecationStats.viewDeprecationsLinkText": "查看弃用", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorDescription": "请在 Kibana 服务器日志中查看错误。", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorTitle": "无法检索 Kibana 弃用", "xpack.upgradeAssistant.kibanaDeprecationErrors.pluginErrorDescription": "请在 Kibana 服务器日志中查看错误。", @@ -25398,23 +25395,12 @@ "xpack.upgradeAssistant.kibanaDeprecationStats.loadingErrorMessage": "检索 Kibana 弃用时发生错误。", "xpack.upgradeAssistant.kibanaDeprecationStats.loadingText": "正在加载 Kibana 弃用统计……", "xpack.upgradeAssistant.kibanaDeprecationStats.statsTitle": "Kibana", - "xpack.upgradeAssistant.kibanaDeprecationStats.totalDeprecationsLabel": "Kibana 总共有 {totalDeprecations} 个弃用", - "xpack.upgradeAssistant.kibanaDeprecationStats.totalDeprecationsTitle": "弃用", - "xpack.upgradeAssistant.kibanaDeprecationStats.viewDeprecationsLinkText": "查看弃用", "xpack.upgradeAssistant.noDeprecationsPrompt.description": "您的配置是最新的。", "xpack.upgradeAssistant.noDeprecationsPrompt.nextStepsDescription": "查看{overviewButton}以了解其他 Stack 弃用。", "xpack.upgradeAssistant.noDeprecationsPrompt.overviewLinkText": "“概览”页面", "xpack.upgradeAssistant.noDeprecationsPrompt.title": "准备好升级!", - "xpack.upgradeAssistant.overview.deprecationLogging.loadingLabel": "正在检索日志记录状态", - "xpack.upgradeAssistant.overview.deprecationLoggingDescription": "启用{deprecationLoggingLink}以查看是否正在使用升级到 Elastic {nextMajor} 后将不再可用的已弃用功能。", - "xpack.upgradeAssistant.overview.deprecationLoggingTitle": "弃用日志", - "xpack.upgradeAssistant.overview.deprecationLogs.disableButtonLabel": "禁用弃用日志记录", "xpack.upgradeAssistant.overview.deprecationLogs.disabledToastMessage": "不记录弃用的操作。", - "xpack.upgradeAssistant.overview.deprecationLogs.disablingButtonLabel": "正在禁用弃用日志记录", - "xpack.upgradeAssistant.overview.deprecationLogs.enableButtonLabel": "启用弃用日志记录", "xpack.upgradeAssistant.overview.deprecationLogs.enabledToastMessage": "记录弃用的操作。", - "xpack.upgradeAssistant.overview.deprecationLogs.enablingButtonLabel": "正在启用弃用日志记录", - "xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorButtonLabel": "弃用日志记录不可用", "xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorMessage": "无法检索日志记录信息。", "xpack.upgradeAssistant.overview.deprecationLogs.reloadButtonLabel": "重试", "xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage": "无法更新日志记录状态。", @@ -25422,9 +25408,6 @@ "xpack.upgradeAssistant.overview.pageDescription": "通过识别弃用的设置并更新配置来准备升级。", "xpack.upgradeAssistant.overview.pageTitle": "升级助手", "xpack.upgradeAssistant.reindex.reindexPrivilegesErrorBatch": "您没有足够的权限重新索引“{indexName}”。", - "xpack.upgradeAssistant.tabs.incompleteCallout.calloutBody.breackingChangesDocButtonLabel": "弃用内容和重大更改", - "xpack.upgradeAssistant.tabs.incompleteCallout.calloutBody.calloutDetail": "Elasticsearch {nextEsVersion} 中的 {breakingChangesDocButton} 完整列表将在最终的 {currentEsVersion} 次要版本中提供。完成列表后,此警告将消失。", - "xpack.upgradeAssistant.tabs.incompleteCallout.calloutTitle": "问题列表可能不完整", "xpack.uptime.addDataButtonLabel": "添加数据", "xpack.uptime.alerts.anomaly.criteriaExpression.ariaLabel": "显示选定监测的条件的表达式。", "xpack.uptime.alerts.anomaly.criteriaExpression.description": "当监测", diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/cluster.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/cluster.test.ts index 6cbaf1cf9dc41..533a74842216a 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/cluster.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/cluster.test.ts @@ -43,7 +43,10 @@ describe('Cluster tab', () => { beforeEach(async () => { httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ isEnabled: true }); + httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ + isDeprecationLogIndexingEnabled: true, + isDeprecationLoggingEnabled: true, + }); await act(async () => { testBed = await setupClusterPage({ isReadOnlyMode: false }); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts index f1f21b430d31b..74fcf14fdf597 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts @@ -7,7 +7,7 @@ import sinon, { SinonFakeServer } from 'sinon'; import { API_BASE_PATH } from '../../../common/constants'; -import { ESUpgradeStatus } from '../../../common/types'; +import { ESUpgradeStatus, DeprecationLoggingStatus } from '../../../common/types'; import { ResponseError } from '../../../public/application/lib/api'; // Register helpers to mock HTTP Requests @@ -24,7 +24,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { }; const setLoadDeprecationLoggingResponse = ( - response?: { isEnabled: boolean }, + response?: DeprecationLoggingStatus, error?: ResponseError ) => { const status = error ? error.statusCode || 400 : 200; @@ -38,7 +38,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { }; const setUpdateDeprecationLoggingResponse = ( - response?: { isEnabled: boolean }, + response?: DeprecationLoggingStatus, error?: ResponseError ) => { const status = error ? error.statusCode || 400 : 200; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/overview.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/overview.helpers.ts index 5c89b6230d85f..93826497b8630 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/overview.helpers.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/overview.helpers.ts @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; -import { DeprecationsOverview } from '../../../public/application/components/overview'; +import { Overview } from '../../../public/application/components/overview'; import { WithAppDependencies } from './setup_environment'; const testBedConfig: TestBedConfig = { @@ -31,7 +31,7 @@ const createActions = (testBed: TestBed) => { const { find, component } = testBed; await act(async () => { - find('upgradeAssistantDeprecationToggle').simulate('click'); + find('deprecationLoggingToggle').simulate('click'); }); component.update(); @@ -43,10 +43,7 @@ const createActions = (testBed: TestBed) => { }; export const setup = async (overrides?: Record): Promise => { - const initTestBed = registerTestBed( - WithAppDependencies(DeprecationsOverview, overrides), - testBedConfig - ); + const initTestBed = registerTestBed(WithAppDependencies(Overview, overrides), testBedConfig); const testBed = await initTestBed(); return { @@ -56,21 +53,27 @@ export const setup = async (overrides?: Record): Promise + Promise.resolve({ + app: '/discover', + path: 'logs', + state: {}, + }) + ), + }, + }, +}; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx index aae5500403322..53b4b5d75931b 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx @@ -14,13 +14,16 @@ import { deprecationsServiceMock, docLinksServiceMock, notificationServiceMock, + applicationServiceMock, } from 'src/core/public/mocks'; import { HttpSetup } from 'src/core/public'; +import { KibanaContextProvider } from '../../../public/shared_imports'; import { mockKibanaSemverVersion } from '../../../common/constants'; import { AppContextProvider } from '../../../public/application/app_context'; import { apiService } from '../../../public/application/lib/api'; import { breadcrumbService } from '../../../public/application/lib/breadcrumbs'; +import { servicesMock } from './services_mock'; import { init as initHttpRequests } from './http_requests'; const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); @@ -39,18 +42,22 @@ export const WithAppDependencies = (Comp: any, overrides: Record '', + getUrlForApp: applicationServiceMock.createStartContract().getUrlForApp, deprecations: deprecationsServiceMock.createStartContract(), }; + const { servicesOverrides, ...contextOverrides } = overrides; + return ( - - - + + + + + ); }; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/indices.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/indices.test.ts index a959473bc4ec6..89f648c98437e 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/indices.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/indices.test.ts @@ -39,7 +39,10 @@ describe('Indices tab', () => { beforeEach(async () => { httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ isEnabled: true }); + httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ + isDeprecationLogIndexingEnabled: true, + isDeprecationLoggingEnabled: true, + }); await act(async () => { testBed = await setupIndicesPage({ isReadOnlyMode: false }); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview.test.ts deleted file mode 100644 index e86917dd4bd5d..0000000000000 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview.test.ts +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { DomainDeprecationDetails } from 'kibana/public'; -import { act } from 'react-dom/test-utils'; -import { deprecationsServiceMock } from 'src/core/public/mocks'; -import { ESUpgradeStatus } from '../../common/types'; - -import { OverviewTestBed, setupOverviewPage, setupEnvironment } from './helpers'; - -describe('Overview page', () => { - let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - beforeEach(async () => { - const esDeprecationsMockResponse: ESUpgradeStatus = { - totalCriticalDeprecations: 1, - cluster: [ - { - level: 'critical', - message: 'Index Lifecycle Management poll interval is set too low', - url: - 'https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html#ilm-poll-interval-limit', - details: - 'The Index Lifecycle Management poll interval setting [indices.lifecycle.poll_interval] is currently set to [500ms], but must be 1s or greater', - }, - ], - indices: [ - { - level: 'warning', - message: 'translog retention settings are ignored', - url: - 'https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html', - details: - 'translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)', - index: 'settings', - }, - ], - }; - - const kibanaDeprecationsMockResponse: DomainDeprecationDetails[] = [ - { - correctiveActions: { manualSteps: ['test-step'] }, - domainId: 'xpack.spaces', - level: 'critical', - message: - 'Disabling the Spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)', - }, - ]; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ isEnabled: true }); - - await act(async () => { - const deprecationService = deprecationsServiceMock.createStartContract(); - deprecationService.getAllDeprecations = jest - .fn() - .mockReturnValue(kibanaDeprecationsMockResponse); - - testBed = await setupOverviewPage({ - deprecations: deprecationService, - }); - }); - - const { component } = testBed; - component.update(); - }); - - afterAll(() => { - server.restore(); - }); - - test('renders the overview page', () => { - const { exists, find } = testBed; - - expect(exists('overviewPageContent')).toBe(true); - - // Verify ES stats - expect(exists('esStatsPanel')).toBe(true); - expect(find('esStatsPanel.totalDeprecations').text()).toContain('2'); - expect(find('esStatsPanel.criticalDeprecations').text()).toContain('1'); - - // Verify Kibana stats - expect(exists('kibanaStatsPanel')).toBe(true); - expect(find('kibanaStatsPanel.totalDeprecations').text()).toContain('1'); - expect(find('kibanaStatsPanel.criticalDeprecations').text()).toContain('1'); - }); - - describe('Deprecation logging', () => { - test('toggles deprecation logging', async () => { - const { find, actions } = testBed; - - httpRequestsMockHelpers.setUpdateDeprecationLoggingResponse({ isEnabled: false }); - - expect(find('upgradeAssistantDeprecationToggle').text()).toEqual( - 'Disable deprecation logging' - ); - - await actions.clickDeprecationToggle(); - - const latestRequest = server.requests[server.requests.length - 1]; - expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({ isEnabled: false }); - expect(find('upgradeAssistantDeprecationToggle').text()).toEqual( - 'Enable deprecation logging' - ); - }); - - test('handles network error when updating logging state', async () => { - const error = { - statusCode: 500, - error: 'Internal server error', - message: 'Internal server error', - }; - - const { actions, find, exists } = testBed; - - httpRequestsMockHelpers.setUpdateDeprecationLoggingResponse(undefined, error); - - expect(find('upgradeAssistantDeprecationToggle').text()).toEqual( - 'Disable deprecation logging' - ); - - await actions.clickDeprecationToggle(); - - // Logging state should not change since there was an error - expect(find('upgradeAssistantDeprecationToggle').text()).toEqual( - 'Disable deprecation logging' - ); - expect(exists('updateLoggingError')).toBe(true); - }); - - test('handles network error when fetching logging state', async () => { - const error = { - statusCode: 500, - error: 'Internal server error', - message: 'Internal server error', - }; - - httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, error); - - await act(async () => { - testBed = await setupOverviewPage(); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(find('upgradeAssistantDeprecationToggle').text()).toEqual( - 'Deprecation logging unavailable' - ); - expect(exists('fetchLoggingError')).toBe(true); - }); - }); - - describe('Error handling', () => { - describe('Kibana deprecations', () => { - test('handles network failure', async () => { - await act(async () => { - const deprecationService = deprecationsServiceMock.createStartContract(); - deprecationService.getAllDeprecations = jest - .fn() - .mockRejectedValue(new Error('Internal Server Error')); - - testBed = await setupOverviewPage({ - deprecations: deprecationService, - }); - }); - - const { component, exists } = testBed; - - component.update(); - - expect(exists('requestErrorIconTip')).toBe(true); - }); - }); - - describe('Elasticsearch deprecations', () => { - test('handles network failure', async () => { - const error = { - statusCode: 500, - error: 'Internal server error', - message: 'Internal server error', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupOverviewPage(); - }); - - const { component, exists } = testBed; - - component.update(); - - expect(exists('requestErrorIconTip')).toBe(true); - }); - - test('handles unauthorized error', async () => { - const error = { - statusCode: 403, - error: 'Forbidden', - message: 'Forbidden', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupOverviewPage(); - }); - - const { component, exists } = testBed; - - component.update(); - - expect(exists('unauthorizedErrorIconTip')).toBe(true); - }); - - test('handles partially upgraded error', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - allNodesUpgraded: false, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupOverviewPage({ isReadOnlyMode: false }); - }); - - const { component, exists } = testBed; - - component.update(); - - expect(exists('partiallyUpgradedErrorIconTip')).toBe(true); - }); - - test('handles upgrade error', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - allNodesUpgraded: true, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupOverviewPage({ isReadOnlyMode: false }); - }); - - const { component, exists } = testBed; - - component.update(); - - expect(exists('upgradedErrorIconTip')).toBe(true); - }); - }); - }); -}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.test.tsx new file mode 100644 index 0000000000000..3db75ba0a342d --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.test.tsx @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; + +import { OverviewTestBed, setupOverviewPage, setupEnvironment } from '../../helpers'; +import { DeprecationLoggingStatus } from '../../../../common/types'; +import { DEPRECATION_LOGS_SOURCE_ID } from '../../../../common/constants'; + +const getLoggingResponse = (toggle: boolean): DeprecationLoggingStatus => ({ + isDeprecationLogIndexingEnabled: toggle, + isDeprecationLoggingEnabled: toggle, +}); + +describe('Overview - Fix deprecation logs step', () => { + let testBed: OverviewTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(getLoggingResponse(true)); + testBed = await setupOverviewPage(); + + const { component } = testBed; + component.update(); + }); + + afterAll(() => { + server.restore(); + }); + + describe('Step 1 - Toggle log writing and collecting', () => { + test('toggles deprecation logging', async () => { + const { find, actions } = testBed; + + httpRequestsMockHelpers.setUpdateDeprecationLoggingResponse({ + isDeprecationLogIndexingEnabled: false, + isDeprecationLoggingEnabled: false, + }); + + expect(find('deprecationLoggingToggle').props()['aria-checked']).toBe(true); + + await actions.clickDeprecationToggle(); + + const latestRequest = server.requests[server.requests.length - 1]; + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({ isEnabled: false }); + expect(find('deprecationLoggingToggle').props()['aria-checked']).toBe(false); + }); + + test('shows callout when only loggerDeprecation is enabled', async () => { + httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ + isDeprecationLogIndexingEnabled: false, + isDeprecationLoggingEnabled: true, + }); + + await act(async () => { + testBed = await setupOverviewPage(); + }); + + const { exists, component } = testBed; + + component.update(); + + expect(exists('deprecationWarningCallout')).toBe(true); + }); + + test('handles network error when updating logging state', async () => { + const error = { + statusCode: 500, + error: 'Internal server error', + message: 'Internal server error', + }; + + const { actions, exists } = testBed; + + httpRequestsMockHelpers.setUpdateDeprecationLoggingResponse(undefined, error); + + await actions.clickDeprecationToggle(); + + expect(exists('updateLoggingError')).toBe(true); + }); + + test('handles network error when fetching logging state', async () => { + const error = { + statusCode: 500, + error: 'Internal server error', + message: 'Internal server error', + }; + + httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, error); + + await act(async () => { + testBed = await setupOverviewPage(); + }); + + const { component, exists } = testBed; + + component.update(); + + expect(exists('fetchLoggingError')).toBe(true); + }); + }); + + describe('Step 2 - Analyze logs', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ + isDeprecationLogIndexingEnabled: true, + isDeprecationLoggingEnabled: true, + }); + }); + + test('Has a link to see logs in observability app', async () => { + await act(async () => { + testBed = await setupOverviewPage({ + http: { + basePath: { + prepend: (url: string) => url, + }, + }, + }); + }); + + const { component, exists, find } = testBed; + + component.update(); + + expect(exists('viewObserveLogs')).toBe(true); + expect(find('viewObserveLogs').props().href).toBe( + `/app/logs/stream?sourceId=${DEPRECATION_LOGS_SOURCE_ID}` + ); + }); + + test('Has a link to see logs in discover app', async () => { + await act(async () => { + testBed = await setupOverviewPage({ + getUrlForApp: jest.fn((app, options) => { + return `${app}/${options.path}`; + }), + }); + }); + + const { exists, component, find } = testBed; + + component.update(); + + expect(exists('viewDiscoverLogs')).toBe(true); + expect(find('viewDiscoverLogs').props().href).toBe('/discover/logs'); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts new file mode 100644 index 0000000000000..ba8f9f8b67d0c --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DomainDeprecationDetails } from 'kibana/public'; +import { ESUpgradeStatus } from '../../../../common/types'; + +export const esDeprecations: ESUpgradeStatus = { + totalCriticalDeprecations: 1, + cluster: [ + { + level: 'critical', + message: 'Index Lifecycle Management poll interval is set too low', + url: + 'https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html#ilm-poll-interval-limit', + details: + 'The Index Lifecycle Management poll interval setting [indices.lifecycle.poll_interval] is currently set to [500ms], but must be 1s or greater', + }, + ], + indices: [ + { + level: 'warning', + message: 'translog retention settings are ignored', + url: + 'https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html', + details: + 'translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)', + index: 'settings', + }, + ], +}; + +export const esDeprecationsEmpty: ESUpgradeStatus = { + totalCriticalDeprecations: 0, + cluster: [], + indices: [], +}; + +export const kibanaDeprecations: DomainDeprecationDetails[] = [ + { + correctiveActions: { manualSteps: ['test-step'] }, + domainId: 'xpack.spaces', + level: 'critical', + message: + 'Disabling the Spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)', + }, + { + correctiveActions: { manualSteps: ['test-step'] }, + domainId: 'xpack.spaces', + level: 'warning', + message: 'Sample warning deprecation', + }, +]; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx new file mode 100644 index 0000000000000..254242ab338a0 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx @@ -0,0 +1,233 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { deprecationsServiceMock } from 'src/core/public/mocks'; + +import * as mockedResponses from './mocked_responses'; +import { OverviewTestBed, setupOverviewPage, setupEnvironment } from '../../helpers'; + +describe('Overview - Fix deprecated settings step', () => { + let testBed: OverviewTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(mockedResponses.esDeprecations); + + await act(async () => { + const deprecationService = deprecationsServiceMock.createStartContract(); + deprecationService.getAllDeprecations = jest + .fn() + .mockReturnValue(mockedResponses.kibanaDeprecations); + + testBed = await setupOverviewPage({ + deprecations: deprecationService, + }); + }); + + const { component } = testBed; + component.update(); + }); + + afterAll(() => { + server.restore(); + }); + + describe('ES deprecations', () => { + test('Shows deprecation warning and critical counts', () => { + const { exists, find } = testBed; + + expect(exists('esStatsPanel')).toBe(true); + expect(find('esStatsPanel.warningDeprecations').text()).toContain('1'); + expect(find('esStatsPanel.criticalDeprecations').text()).toContain('1'); + }); + + test('Handles network failure', async () => { + const error = { + statusCode: 500, + error: 'Cant retrieve deprecations error', + message: 'Cant retrieve deprecations error', + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupOverviewPage(); + }); + + const { component, exists } = testBed; + + component.update(); + + expect(exists('esRequestErrorIconTip')).toBe(true); + }); + + test('Hides deprecation counts if it doesnt have any', async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(mockedResponses.esDeprecationsEmpty); + + await act(async () => { + testBed = await setupOverviewPage(); + }); + + const { exists } = testBed; + + expect(exists('noDeprecationsLabel')).toBe(true); + }); + + test('Stats panel navigates to deprecations list if clicked', () => { + const { component, exists, find } = testBed; + + component.update(); + + expect(exists('esStatsPanel')).toBe(true); + expect(find('esStatsPanel').find('a').props().href).toBe('/es_deprecations/cluster'); + }); + + describe('Renders ES errors', () => { + test('handles network failure', async () => { + const error = { + statusCode: 500, + error: 'Internal server error', + message: 'Internal server error', + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupOverviewPage(); + }); + + const { component, exists } = testBed; + + component.update(); + + expect(exists('esRequestErrorIconTip')).toBe(true); + }); + + test('handles unauthorized error', async () => { + const error = { + statusCode: 403, + error: 'Forbidden', + message: 'Forbidden', + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupOverviewPage(); + }); + + const { component, exists } = testBed; + + component.update(); + + expect(exists('unauthorizedErrorIconTip')).toBe(true); + }); + + test('handles partially upgraded error', async () => { + const error = { + statusCode: 426, + error: 'Upgrade required', + message: 'There are some nodes running a different version of Elasticsearch', + attributes: { + allNodesUpgraded: false, + }, + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupOverviewPage({ isReadOnlyMode: false }); + }); + + const { component, exists } = testBed; + + component.update(); + + expect(exists('partiallyUpgradedErrorIconTip')).toBe(true); + }); + + test('handles upgrade error', async () => { + const error = { + statusCode: 426, + error: 'Upgrade required', + message: 'There are some nodes running a different version of Elasticsearch', + attributes: { + allNodesUpgraded: true, + }, + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupOverviewPage({ isReadOnlyMode: false }); + }); + + const { component, exists } = testBed; + + component.update(); + + expect(exists('upgradedErrorIconTip')).toBe(true); + }); + }); + }); + + describe('Kibana deprecations', () => { + test('Show deprecation warning and critical counts', () => { + const { exists, find } = testBed; + + expect(exists('kibanaStatsPanel')).toBe(true); + expect(find('kibanaStatsPanel.warningDeprecations').text()).toContain('1'); + expect(find('kibanaStatsPanel.criticalDeprecations').text()).toContain('1'); + }); + + test('Handles network failure', async () => { + await act(async () => { + const deprecationService = deprecationsServiceMock.createStartContract(); + deprecationService.getAllDeprecations = jest + .fn() + .mockRejectedValue(new Error('Internal Server Error')); + + testBed = await setupOverviewPage({ + deprecations: deprecationService, + }); + }); + + const { component, exists } = testBed; + + component.update(); + + expect(exists('kibanaRequestErrorIconTip')).toBe(true); + }); + + test('Hides deprecation count if it doesnt have any', async () => { + await act(async () => { + const deprecationService = deprecationsServiceMock.createStartContract(); + deprecationService.getAllDeprecations = jest.fn().mockRejectedValue([]); + + testBed = await setupOverviewPage({ + deprecations: deprecationService, + }); + }); + + const { exists } = testBed; + + expect(exists('noDeprecationsLabel')).toBe(true); + expect(exists('kibanaStatsPanel.warningDeprecations')).toBe(false); + expect(exists('kibanaStatsPanel.criticalDeprecations')).toBe(false); + }); + + test('Stats panel navigates to deprecations list if clicked', () => { + const { component, exists, find } = testBed; + + component.update(); + + expect(exists('kibanaStatsPanel')).toBe(true); + expect(find('kibanaStatsPanel').find('a').props().href).toBe('/kibana_deprecations'); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx new file mode 100644 index 0000000000000..21daed29acaca --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; + +import { OverviewTestBed, setupOverviewPage, setupEnvironment } from '../../helpers'; + +describe('Overview - Upgrade Step', () => { + let testBed: OverviewTestBed; + const { server } = setupEnvironment(); + + beforeEach(async () => { + testBed = await setupOverviewPage(); + testBed.component.update(); + }); + + afterAll(() => { + server.restore(); + }); + + describe('Step 3 - Upgrade stack', () => { + test('Shows link to setup upgrade docs for on-prem installations', () => { + const { exists } = testBed; + + expect(exists('upgradeSetupDocsLink')).toBe(true); + expect(exists('upgradeSetupCloudLink')).toBe(false); + }); + + test('Shows upgrade cta and link to docs for cloud installations', async () => { + await act(async () => { + testBed = await setupOverviewPage({ + servicesOverrides: { + cloud: { + isCloudEnabled: true, + baseUrl: 'https://test.com', + cloudId: '1234', + }, + }, + }); + }); + + const { component, exists, find } = testBed; + component.update(); + + expect(exists('upgradeSetupCloudLink')).toBe(true); + expect(exists('upgradeSetupDocsLink')).toBe(true); + + expect(find('upgradeSetupCloudLink').props().href).toBe('https://test.com/deployments/1234'); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/common/constants.ts b/x-pack/plugins/upgrade_assistant/common/constants.ts index bab3d8c3fda86..893d61d329534 100644 --- a/x-pack/plugins/upgrade_assistant/common/constants.ts +++ b/x-pack/plugins/upgrade_assistant/common/constants.ts @@ -27,3 +27,7 @@ export const indexSettingDeprecations = { }; export const API_BASE_PATH = '/api/upgrade_assistant'; + +export const DEPRECATION_WARNING_UPPER_LIMIT = 999999; +export const DEPRECATION_LOGS_SOURCE_ID = 'deprecation_logs'; +export const DEPRECATION_LOGS_INDEX_PATTERN = '.logs-deprecation.elasticsearch-default'; diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts index 680e1e03dbbf0..35c514a0a95bb 100644 --- a/x-pack/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/plugins/upgrade_assistant/common/types.ts @@ -248,3 +248,8 @@ export interface MlOperation extends SavedObjectAttributes { snapshotId: string; jobId: string; } + +export interface DeprecationLoggingStatus { + isDeprecationLogIndexingEnabled: boolean; + isDeprecationLoggingEnabled: boolean; +} diff --git a/x-pack/plugins/upgrade_assistant/kibana.json b/x-pack/plugins/upgrade_assistant/kibana.json index e69e352104f35..e66f25318a28c 100644 --- a/x-pack/plugins/upgrade_assistant/kibana.json +++ b/x-pack/plugins/upgrade_assistant/kibana.json @@ -8,7 +8,7 @@ "githubTeam": "kibana-stack-management" }, "configPath": ["xpack", "upgrade_assistant"], - "requiredPlugins": ["management", "licensing", "features"], - "optionalPlugins": ["usageCollection"], + "requiredPlugins": ["management", "discover", "data", "licensing", "features", "infra"], + "optionalPlugins": ["usageCollection", "cloud"], "requiredBundles": ["esUiShared", "kibanaReact"] } diff --git a/x-pack/plugins/upgrade_assistant/public/application/app.tsx b/x-pack/plugins/upgrade_assistant/public/application/app.tsx index 8086d3322c0e9..b1571b9e45461 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/app.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/app.tsx @@ -8,15 +8,22 @@ import React from 'react'; import { Router, Switch, Route, Redirect } from 'react-router-dom'; import { I18nStart, ScopedHistory } from 'src/core/public'; + +import { ApplicationStart } from 'kibana/public'; +import { KibanaContextProvider } from '../shared_imports'; +import { AppServicesContext } from '../types'; import { AppContextProvider, ContextValue, useAppContext } from './app_context'; import { ComingSoonPrompt } from './components/coming_soon_prompt'; import { EsDeprecationsContent } from './components/es_deprecations'; import { KibanaDeprecationsContent } from './components/kibana_deprecations'; -import { DeprecationsOverview } from './components/overview'; +import { Overview } from './components/overview'; +import { RedirectAppLinks } from '../../../../../src/plugins/kibana_react/public'; export interface AppDependencies extends ContextValue { i18n: I18nStart; history: ScopedHistory; + application: ApplicationStart; + services: AppServicesContext; } const App: React.FunctionComponent = () => { @@ -29,7 +36,7 @@ const App: React.FunctionComponent = () => { return ( - + @@ -45,12 +52,22 @@ export const AppWithRouter = ({ history }: { history: ScopedHistory }) => { ); }; -export const RootComponent = ({ i18n, history, ...contextValue }: AppDependencies) => { +export const RootComponent = ({ + i18n, + history, + services, + application, + ...contextValue +}: AppDependencies) => { return ( - - - - - + + + + + + + + + ); }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/latest_minor_banner.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/latest_minor_banner.tsx deleted file mode 100644 index 4fd62417c73e3..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/latest_minor_banner.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { EuiCallOut, EuiLink } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { useAppContext } from '../app_context'; - -export const LatestMinorBanner: React.FunctionComponent = () => { - const { docLinks, kibanaVersionInfo } = useAppContext(); - - const { ELASTIC_WEBSITE_URL } = docLinks; - const esDocBasePath = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference`; - - const { currentMajor, nextMajor } = kibanaVersionInfo; - - return ( - - } - color="warning" - iconType="help" - > -

- - - - ), - nextEsVersion: `${nextMajor}.x`, - currentEsVersion: `${currentMajor}.x`, - }} - /> -

-
- ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/overview/_index.scss index c64a8f5a5326d..cbcfbff3bab68 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/_index.scss +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/_index.scss @@ -1 +1,2 @@ -@import 'steps'; +@import 'review_logs_step/index'; +@import 'fix_deprecation_logs_step/index'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/_steps.scss b/x-pack/plugins/upgrade_assistant/public/application/components/overview/_steps.scss deleted file mode 100644 index 789c22fe1c28d..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/_steps.scss +++ /dev/null @@ -1,6 +0,0 @@ -.upgSteps { - .euiStep:last-child .euiStep__content { - padding-bottom: 0; - margin-bottom: 0; - } -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/deprecation_logging_toggle.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/deprecation_logging_toggle.tsx deleted file mode 100644 index ab2f94ee58240..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/deprecation_logging_toggle.tsx +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useEffect, useState } from 'react'; - -import { - EuiButton, - EuiFlexItem, - EuiFlexGroup, - EuiText, - EuiTextColor, - EuiButtonEmpty, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { useAppContext } from '../../app_context'; -import { ResponseError } from '../../lib/api'; - -const i18nTexts = { - fetchErrorMessage: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorMessage', - { - defaultMessage: 'Could not retrieve logging information.', - } - ), - reloadButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.reloadButtonLabel', - { - defaultMessage: 'Try again', - } - ), - updateErrorMessage: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage', - { - defaultMessage: 'Could not update logging state.', - } - ), - enabledMessage: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.enabledToastMessage', - { - defaultMessage: 'Log deprecated actions.', - } - ), - disabledMessage: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.disabledToastMessage', - { - defaultMessage: 'Do not log deprecated actions.', - } - ), - fetchButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogging.loadingLabel', - { - defaultMessage: 'Retrieving logging state', - } - ), - enablingButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.enablingButtonLabel', - { - defaultMessage: 'Enabling deprecation logging', - } - ), - disablingButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.disablingButtonLabel', - { - defaultMessage: 'Disabling deprecation logging', - } - ), - enableButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.enableButtonLabel', - { - defaultMessage: 'Enable deprecation logging', - } - ), - disableButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.disableButtonLabel', - { - defaultMessage: 'Disable deprecation logging', - } - ), - fetchErrorButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorButtonLabel', - { - defaultMessage: 'Deprecation logging unavailable', - } - ), -}; - -export const DeprecationLoggingToggle: React.FunctionComponent = () => { - const { api, notifications } = useAppContext(); - - const { data, error: fetchError, isLoading, resendRequest } = api.useLoadDeprecationLogging(); - - const [isEnabled, setIsEnabled] = useState(undefined); - const [isUpdating, setIsUpdating] = useState(false); - const [updateError, setUpdateError] = useState(undefined); - - const getButtonLabel = () => { - if (isLoading) { - return i18nTexts.fetchButtonLabel; - } - - if (isUpdating) { - return isEnabled ? i18nTexts.disablingButtonLabel : i18nTexts.enablingButtonLabel; - } - - if (fetchError) { - return i18nTexts.fetchErrorButtonLabel; - } - - if (isEnabled) { - return i18nTexts.disableButtonLabel; - } - - return i18nTexts.enableButtonLabel; - }; - - useEffect(() => { - if (isLoading === false && data) { - setIsEnabled(data.isEnabled); - } - }, [data, isLoading]); - - const toggleLogging = async () => { - const newIsEnabledValue = !isEnabled; - - setIsUpdating(true); - - const { - data: updatedLoggingState, - error: updateDeprecationError, - } = await api.updateDeprecationLogging({ - isEnabled: newIsEnabledValue, - }); - - setIsUpdating(false); - - if (updateDeprecationError) { - setUpdateError(updateDeprecationError); - } else if (updatedLoggingState) { - setIsEnabled(updatedLoggingState.isEnabled); - notifications.toasts.addSuccess( - updatedLoggingState.isEnabled ? i18nTexts.enabledMessage : i18nTexts.disabledMessage - ); - } - }; - - return ( - - - - {getButtonLabel()} - - - - {fetchError && ( - - -

- {i18nTexts.fetchErrorMessage} - {fetchError.statusCode && fetchError.message && ( - <> - {' '} - {`${fetchError.statusCode}: ${fetchError.message}`} - - )}{' '} - - {i18nTexts.reloadButtonLabel} - -

-
-
- )} - - {updateError && ( - - -

- {i18nTexts.updateErrorMessage} - {updateError.statusCode && updateError.message && ( - <> - {' '} - {`${updateError.statusCode}: ${updateError.message}`} - - )} -

-
-
- )} -
- ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/es_stats.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/es_stats.tsx deleted file mode 100644 index 3152639d3f10d..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/es_stats.tsx +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FunctionComponent } from 'react'; - -import { - EuiLink, - EuiPanel, - EuiStat, - EuiTitle, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiIconTip, - EuiScreenReaderOnly, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { RouteComponentProps } from 'react-router-dom'; -import { reactRouterNavigate } from '../../../../../../../src/plugins/kibana_react/public'; -import { useAppContext } from '../../app_context'; -import { EsStatsErrors } from './es_stats_error'; - -const i18nTexts = { - statsTitle: i18n.translate('xpack.upgradeAssistant.esDeprecationStats.statsTitle', { - defaultMessage: 'Elasticsearch', - }), - totalDeprecationsTitle: i18n.translate( - 'xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTitle', - { - defaultMessage: 'Deprecations', - } - ), - criticalDeprecationsTitle: i18n.translate( - 'xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsTitle', - { - defaultMessage: 'Critical', - } - ), - viewDeprecationsLink: i18n.translate( - 'xpack.upgradeAssistant.esDeprecationStats.viewDeprecationsLinkText', - { - defaultMessage: 'View deprecations', - } - ), - loadingText: i18n.translate('xpack.upgradeAssistant.esDeprecationStats.loadingText', { - defaultMessage: 'Loading Elasticsearch deprecation stats…', - }), - getCriticalDeprecationsMessage: (criticalDeprecations: number) => - i18n.translate('xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsLabel', { - defaultMessage: 'This cluster has {criticalDeprecations} critical deprecations', - values: { - criticalDeprecations, - }, - }), - getTotalDeprecationsTooltip: (clusterCount: number, indexCount: number) => - i18n.translate('xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTooltip', { - defaultMessage: - 'This cluster is using {clusterCount} deprecated cluster settings and {indexCount} deprecated index settings', - values: { - clusterCount, - indexCount, - }, - }), -}; - -interface Props { - history: RouteComponentProps['history']; -} - -export const ESDeprecationStats: FunctionComponent = ({ history }) => { - const { api } = useAppContext(); - - const { data: esDeprecations, isLoading, error } = api.useLoadUpgradeStatus(); - - const allDeprecations = esDeprecations?.cluster?.concat(esDeprecations?.indices) ?? []; - const criticalDeprecations = allDeprecations.filter( - (deprecation) => deprecation.level === 'critical' - ); - - return ( - - - - -

{i18nTexts.statsTitle}

-
-
- - - {i18nTexts.viewDeprecationsLink} - - -
- - - - - - - {i18nTexts.totalDeprecationsTitle}{' '} - - - } - isLoading={isLoading} - > - {error === null && ( - -

- {isLoading - ? i18nTexts.loadingText - : i18nTexts.getTotalDeprecationsTooltip( - esDeprecations?.cluster.length ?? 0, - esDeprecations?.indices.length ?? 0 - )} -

-
- )} -
-
- - - - {error === null && ( - -

- {isLoading - ? i18nTexts.loadingText - : i18nTexts.getCriticalDeprecationsMessage(criticalDeprecations.length)} -

-
- )} - - {error && } -
-
-
-
- ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/_index.scss new file mode 100644 index 0000000000000..2299c08a4ac31 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/_index.scss @@ -0,0 +1 @@ +@import 'deprecation_logging_toggle/deprecation_logging_toggle'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/_deprecation_logging_toggle.scss b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/_deprecation_logging_toggle.scss new file mode 100644 index 0000000000000..e8b6ec06ed7c9 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/_deprecation_logging_toggle.scss @@ -0,0 +1,7 @@ +// When you have a flex item with an EuiSwitch and replace it with +// an EuiLoadingSpinner you end up with a slight 2px difference between +// them. With this selector we offset the difference so that the content +// of the page doesnt jump when toggling between states. +.upgToggleLoading > .upgLoadingItem { + margin: $euiSizeM / 2; +} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx new file mode 100644 index 0000000000000..42b9f073a52f1 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, FunctionComponent } from 'react'; + +import { + EuiSwitch, + EuiFlexItem, + EuiFlexGroup, + EuiText, + EuiPopover, + EuiLink, + EuiTextColor, + EuiButtonEmpty, + EuiLoadingSpinner, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { ResponseError } from '../../../../lib/api'; +import { DeprecationLoggingPreviewProps } from '../../../types'; + +const i18nTexts = { + fetchErrorMessage: i18n.translate( + 'xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorMessage', + { + defaultMessage: 'Could not retrieve logging information.', + } + ), + reloadButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.overview.deprecationLogs.reloadButtonLabel', + { + defaultMessage: 'Try again', + } + ), + updateErrorMessage: i18n.translate( + 'xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage', + { + defaultMessage: 'Could not update logging state.', + } + ), + errorLabel: i18n.translate('xpack.upgradeAssistant.overview.deprecationLogs.errorLabel', { + defaultMessage: 'Error', + }), + buttonLabel: i18n.translate('xpack.upgradeAssistant.overview.deprecationLogs.buttonLabel', { + defaultMessage: 'Enable deprecation logging and indexing', + }), + loadingLogsLabel: i18n.translate('xpack.upgradeAssistant.overview.loadingLogsLabel', { + defaultMessage: 'Loading log collection state…', + }), +}; + +const ErrorDetailsLink = ({ error }: { error: ResponseError }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = () => setIsPopoverOpen(!isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + + if (!error.statusCode || !error.message) { + return null; + } + + const button = ( + + {i18nTexts.errorLabel} {error.statusCode} + + ); + + return ( + + +

{error.message}

+
+
+ ); +}; + +export const DeprecationLoggingToggle: FunctionComponent = ({ + isDeprecationLogIndexingEnabled, + isLoading, + isUpdating, + fetchError, + updateError, + resendRequest, + toggleLogging, +}) => { + if (isLoading) { + return ( + + + + + {i18nTexts.loadingLogsLabel} + + ); + } + + if (fetchError) { + return ( + + + + + {i18nTexts.fetchErrorMessage} + + + + + + + + + {i18nTexts.reloadButtonLabel} + + + + ); + } + + return ( + + + + + + {updateError && ( + + + + {i18nTexts.updateErrorMessage} + + + + + + + )} + + {isUpdating && ( + + + + )} + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/index.ts new file mode 100644 index 0000000000000..f41ab454f894e --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/deprecation_logging_toggle/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { DeprecationLoggingToggle } from './deprecation_logging_toggle'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/external_links.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/external_links.tsx new file mode 100644 index 0000000000000..0cd5ad5bfdb2f --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/external_links.tsx @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent, useState, useEffect } from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiLink, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiPanel, EuiText } from '@elastic/eui'; + +import { useAppContext } from '../../../app_context'; +import { useKibana, DataPublicPluginStart } from '../../../../shared_imports'; +import { + DEPRECATION_LOGS_INDEX_PATTERN, + DEPRECATION_LOGS_SOURCE_ID, +} from '../../../../../common/constants'; + +const getDeprecationIndexPatternId = async (dataService: DataPublicPluginStart) => { + const { indexPatterns: indexPatternService } = dataService; + + const results = await indexPatternService.find(DEPRECATION_LOGS_INDEX_PATTERN); + // Since the find might return also results with wildcard matchers we need to find the + // index pattern that has an exact match with our title. + const deprecationIndexPattern = results.find( + (result) => result.title === DEPRECATION_LOGS_INDEX_PATTERN + ); + + if (deprecationIndexPattern) { + return deprecationIndexPattern.id; + } else { + const newIndexPattern = await indexPatternService.createAndSave({ + title: DEPRECATION_LOGS_INDEX_PATTERN, + allowNoIndex: true, + }); + return newIndexPattern.id; + } +}; + +const DiscoverAppLink: FunctionComponent = () => { + const { getUrlForApp } = useAppContext(); + const { data: dataService, discover: discoverService } = useKibana().services; + + const [discoveryUrl, setDiscoveryUrl] = useState(); + + useEffect(() => { + const getDiscoveryUrl = async () => { + const indexPatternId = await getDeprecationIndexPatternId(dataService); + const appLocation = await discoverService?.locator?.getLocation({ indexPatternId }); + + const result = getUrlForApp(appLocation?.app as string, { + path: appLocation?.path, + }); + setDiscoveryUrl(result); + }; + + getDiscoveryUrl(); + }, [dataService, discoverService, getUrlForApp]); + + return ( + + + + ); +}; + +const ObservabilityAppLink: FunctionComponent = () => { + const { http } = useAppContext(); + const logStreamUrl = http?.basePath?.prepend( + `/app/logs/stream?sourceId=${DEPRECATION_LOGS_SOURCE_ID}` + ); + + return ( + + + + ); +}; + +export const ExternalLinks: FunctionComponent = () => { + return ( + + + + +

+ +

+
+ + +
+
+ + + +

+ +

+
+ + +
+
+
+ ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.tsx new file mode 100644 index 0000000000000..a2f1feae4979d --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/fix_deprecation_logs_step.tsx @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { EuiText, EuiSpacer, EuiPanel, EuiCallOut } from '@elastic/eui'; +import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; + +import { ExternalLinks } from './external_links'; +import { useDeprecationLogging } from './use_deprecation_logging'; +import { DeprecationLoggingToggle } from './deprecation_logging_toggle'; + +const i18nTexts = { + identifyStepTitle: i18n.translate('xpack.upgradeAssistant.overview.identifyStepTitle', { + defaultMessage: 'Identify deprecated API use and update your applications', + }), + toggleTitle: i18n.translate('xpack.upgradeAssistant.overview.toggleTitle', { + defaultMessage: 'Log Elasticsearch deprecation warnings', + }), + analyzeTitle: i18n.translate('xpack.upgradeAssistant.overview.analyzeTitle', { + defaultMessage: 'Analyze deprecation logs', + }), + onlyLogWritingEnabledTitle: i18n.translate( + 'xpack.upgradeAssistant.overview.deprecationLogs.deprecationWarningTitle', + { + defaultMessage: 'Your logs are being written to the logs directory', + } + ), + onlyLogWritingEnabledBody: i18n.translate( + 'xpack.upgradeAssistant.overview.deprecationLogs.deprecationWarningBody', + { + defaultMessage: + 'Go to your logs directory to view the deprecation logs or enable log collecting to see them in the UI.', + } + ), +}; + +const DeprecationLogsPreview: FunctionComponent = () => { + const state = useDeprecationLogging(); + + return ( + <> + +

{i18nTexts.toggleTitle}

+
+ + + + + + {state.onlyDeprecationLogWritingEnabled && ( + <> + + +

{i18nTexts.onlyLogWritingEnabledBody}

+
+ + )} + + {state.isDeprecationLogIndexingEnabled && ( + <> + + +

{i18nTexts.analyzeTitle}

+
+ + + + )} + + ); +}; + +export const getFixDeprecationLogsStep = (): EuiStepProps => { + return { + title: i18nTexts.identifyStepTitle, + status: 'incomplete', + children: , + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/index.ts new file mode 100644 index 0000000000000..d4794623d8a99 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getFixDeprecationLogsStep } from './fix_deprecation_logs_step'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/use_deprecation_logging.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/use_deprecation_logging.ts new file mode 100644 index 0000000000000..5545eb0353741 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_deprecation_logs_step/use_deprecation_logging.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useEffect } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { useAppContext } from '../../../app_context'; +import { ResponseError } from '../../../lib/api'; +import { DeprecationLoggingPreviewProps } from '../../types'; + +const i18nTexts = { + enabledMessage: i18n.translate( + 'xpack.upgradeAssistant.overview.deprecationLogs.enabledToastMessage', + { + defaultMessage: 'Deprecated API requests will be logged and indexed.', + } + ), + disabledMessage: i18n.translate( + 'xpack.upgradeAssistant.overview.deprecationLogs.disabledToastMessage', + { + defaultMessage: 'Deprecated API requests will not be logged.', + } + ), +}; + +export const useDeprecationLogging = (): DeprecationLoggingPreviewProps => { + const { api, notifications } = useAppContext(); + const { data, error: fetchError, isLoading, resendRequest } = api.useLoadDeprecationLogging(); + + const [isDeprecationLogIndexingEnabled, setIsDeprecationLogIndexingEnabled] = useState(false); + const [isUpdating, setIsUpdating] = useState(false); + const [onlyDeprecationLogWritingEnabled, setOnlyDeprecationLogWritingEnabled] = useState(false); + const [updateError, setUpdateError] = useState(); + + useEffect(() => { + if (!isLoading && data) { + const { + isDeprecationLogIndexingEnabled: isIndexingEnabled, + isDeprecationLoggingEnabled, + } = data; + setIsDeprecationLogIndexingEnabled(isIndexingEnabled); + + if (!isIndexingEnabled && isDeprecationLoggingEnabled) { + setOnlyDeprecationLogWritingEnabled(true); + } + } + }, [data, isLoading]); + + const toggleLogging = async () => { + setIsUpdating(true); + + const { + data: updatedLoggingState, + error: updateDeprecationError, + } = await api.updateDeprecationLogging({ + isEnabled: !isDeprecationLogIndexingEnabled, + }); + + setIsUpdating(false); + setOnlyDeprecationLogWritingEnabled(false); + + if (updateDeprecationError) { + setUpdateError(updateDeprecationError); + } else if (updatedLoggingState) { + setIsDeprecationLogIndexingEnabled(updatedLoggingState.isDeprecationLogIndexingEnabled); + notifications.toasts.addSuccess( + updatedLoggingState.isDeprecationLogIndexingEnabled + ? i18nTexts.enabledMessage + : i18nTexts.disabledMessage + ); + } + }; + + return { + isDeprecationLogIndexingEnabled, + isLoading, + isUpdating, + toggleLogging, + fetchError, + updateError, + resendRequest, + onlyDeprecationLogWritingEnabled, + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/index.ts index a64d7b0d44915..69c843fe3821e 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/index.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { DeprecationsOverview } from './overview'; +export { Overview } from './overview'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/kibana_stats.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/kibana_stats.tsx deleted file mode 100644 index 28941d1305adf..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/kibana_stats.tsx +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FunctionComponent, useEffect, useState } from 'react'; - -import { - EuiLink, - EuiPanel, - EuiStat, - EuiTitle, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiIconTip, - EuiScreenReaderOnly, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { RouteComponentProps } from 'react-router-dom'; -import type { DomainDeprecationDetails } from 'kibana/public'; -import { reactRouterNavigate } from '../../../../../../../src/plugins/kibana_react/public'; -import { useAppContext } from '../../app_context'; - -const i18nTexts = { - statsTitle: i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.statsTitle', { - defaultMessage: 'Kibana', - }), - totalDeprecationsTitle: i18n.translate( - 'xpack.upgradeAssistant.kibanaDeprecationStats.totalDeprecationsTitle', - { - defaultMessage: 'Deprecations', - } - ), - criticalDeprecationsTitle: i18n.translate( - 'xpack.upgradeAssistant.kibanaDeprecationStats.criticalDeprecationsTitle', - { - defaultMessage: 'Critical', - } - ), - viewDeprecationsLink: i18n.translate( - 'xpack.upgradeAssistant.kibanaDeprecationStats.viewDeprecationsLinkText', - { - defaultMessage: 'View deprecations', - } - ), - loadingError: i18n.translate( - 'xpack.upgradeAssistant.kibanaDeprecationStats.loadingErrorMessage', - { - defaultMessage: 'An error occurred while retrieving Kibana deprecations.', - } - ), - loadingText: i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.loadingText', { - defaultMessage: 'Loading Kibana deprecation stats…', - }), - getCriticalDeprecationsMessage: (criticalDeprecations: number) => - i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.criticalDeprecationsLabel', { - defaultMessage: 'Kibana has {criticalDeprecations} critical deprecations', - values: { - criticalDeprecations, - }, - }), - getTotalDeprecationsMessage: (totalDeprecations: number) => - i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.totalDeprecationsLabel', { - defaultMessage: 'Kibana has {totalDeprecations} total deprecations', - values: { - totalDeprecations, - }, - }), -}; - -interface Props { - history: RouteComponentProps['history']; -} - -export const KibanaDeprecationStats: FunctionComponent = ({ history }) => { - const { deprecations } = useAppContext(); - - const [kibanaDeprecations, setKibanaDeprecations] = useState< - DomainDeprecationDetails[] | undefined - >(undefined); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(undefined); - - useEffect(() => { - async function getAllDeprecations() { - setIsLoading(true); - - try { - const response = await deprecations.getAllDeprecations(); - setKibanaDeprecations(response); - } catch (e) { - setError(e); - } - - setIsLoading(false); - } - - getAllDeprecations(); - }, [deprecations]); - - return ( - - - - -

{i18nTexts.statsTitle}

-
-
- - - {i18nTexts.viewDeprecationsLink} - - -
- - - - - - - {error === undefined && ( - -

- {isLoading - ? i18nTexts.loadingText - : i18nTexts.getTotalDeprecationsMessage(kibanaDeprecations?.length ?? 0)} -

-
- )} -
-
- - - deprecation.level === 'critical') - ?.length ?? '0' - : '--' - } - description={i18nTexts.criticalDeprecationsTitle} - titleColor="danger" - isLoading={isLoading} - > - {error === undefined && ( - -

- {isLoading - ? i18nTexts.loadingText - : i18nTexts.getCriticalDeprecationsMessage( - kibanaDeprecations - ? kibanaDeprecations.filter( - (deprecation) => deprecation.level === 'critical' - )?.length ?? 0 - : 0 - )} -

-
- )} - - {error && ( - <> - - - - - )} -
-
-
-
- ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx index b77d4a337295c..bd076dd600216 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx @@ -8,71 +8,26 @@ import React, { FunctionComponent, useEffect } from 'react'; import { - EuiPageContentBody, + EuiSteps, EuiText, EuiPageHeader, EuiButtonEmpty, - EuiFlexItem, - EuiFlexGroup, EuiSpacer, - EuiTitle, EuiLink, + EuiPageBody, + EuiPageContent, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { RouteComponentProps } from 'react-router-dom'; import { useAppContext } from '../../app_context'; -import { LatestMinorBanner } from '../latest_minor_banner'; -import { ESDeprecationStats } from './es_stats'; -import { KibanaDeprecationStats } from './kibana_stats'; -import { DeprecationLoggingToggle } from './deprecation_logging_toggle'; +import { getReviewLogsStep } from './review_logs_step'; +import { getFixDeprecationLogsStep } from './fix_deprecation_logs_step'; +import { getUpgradeStep } from './upgrade_step'; -const i18nTexts = { - pageTitle: i18n.translate('xpack.upgradeAssistant.overview.pageTitle', { - defaultMessage: 'Upgrade Assistant', - }), - pageDescription: i18n.translate('xpack.upgradeAssistant.overview.pageDescription', { - defaultMessage: - 'Prepare to upgrade by identifying deprecated settings and updating your configuration.', - }), - docLink: i18n.translate('xpack.upgradeAssistant.overview.documentationLinkText', { - defaultMessage: 'Documentation', - }), - deprecationLoggingTitle: i18n.translate( - 'xpack.upgradeAssistant.overview.deprecationLoggingTitle', - { - defaultMessage: 'Deprecation logs', - } - ), - getDeprecationLoggingDescription: (nextMajor: string, href: string) => ( - - {i18n.translate( - 'xpack.upgradeAssistant.deprecationLoggingDescription.deprecationLoggingLink', - { - defaultMessage: 'deprecation logging', - } - )} - - ), - }} - /> - ), -}; - -interface Props { - history: RouteComponentProps['history']; -} - -export const DeprecationsOverview: FunctionComponent = ({ history }) => { +export const Overview: FunctionComponent = () => { const { kibanaVersionInfo, breadcrumbs, docLinks, api } = useAppContext(); - const { nextMajor } = kibanaVersionInfo; + const { currentMajor } = kibanaVersionInfo; useEffect(() => { async function sendTelemetryData() { @@ -89,68 +44,51 @@ export const DeprecationsOverview: FunctionComponent = ({ history }) => { }, [breadcrumbs]); return ( -
- - {i18nTexts.docLink} - , - ]} - /> - - - - - <> - {/* Remove this in last minor of the current major (e.g., 7.15) */} - - - - - {/* Deprecation stats */} - - - - - - - - - - - - - {/* Deprecation logging */} - - - -

{i18nTexts.deprecationLoggingTitle}

-
- - -

- {i18nTexts.getDeprecationLoggingDescription( - `${nextMajor}.x`, - docLinks.links.elasticsearch.deprecationLogging - )} -

-
- - - - -
-
- -
-
+ + + + + , + ]} + > + + + + + + + + + + + + ); }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_index.scss new file mode 100644 index 0000000000000..7eea518d5698e --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_index.scss @@ -0,0 +1,2 @@ +@import 'stats_panel'; +@import 'no_deprecations/no_deprecations'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_stats_panel.scss b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_stats_panel.scss new file mode 100644 index 0000000000000..b32f3eb9ddbdf --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/_stats_panel.scss @@ -0,0 +1,6 @@ +// Used by both es_stats and kibana_stats panel for having the EuiPopover Icon +// for errors shown next to the title without having to resort to wrapping everything +// with EuiFlexGroups. +.upgWarningIcon { + margin-left: $euiSizeS; +} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx new file mode 100644 index 0000000000000..97306dac287ba --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { + EuiStat, + EuiSpacer, + EuiFlexGroup, + EuiFlexItem, + EuiCard, + EuiScreenReaderOnly, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { reactRouterNavigate } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { getDeprecationsUpperLimit } from '../../../../lib/utils'; +import { useAppContext } from '../../../../app_context'; +import { EsStatsErrors } from './es_stats_error'; +import { NoDeprecations } from '../no_deprecations'; + +const i18nTexts = { + statsTitle: i18n.translate('xpack.upgradeAssistant.esDeprecationStats.statsTitle', { + defaultMessage: 'Elasticsearch', + }), + warningDeprecationsTitle: i18n.translate( + 'xpack.upgradeAssistant.esDeprecationStats.warningDeprecationsTitle', + { + defaultMessage: 'Warning', + } + ), + criticalDeprecationsTitle: i18n.translate( + 'xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsTitle', + { + defaultMessage: 'Critical', + } + ), + loadingText: i18n.translate('xpack.upgradeAssistant.esDeprecationStats.loadingText', { + defaultMessage: 'Loading Elasticsearch deprecation stats…', + }), + getCriticalDeprecationsMessage: (criticalDeprecations: number) => + i18n.translate('xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsLabel', { + defaultMessage: 'This cluster has {criticalDeprecations} critical deprecations', + values: { + criticalDeprecations, + }, + }), + getWarningDeprecationMessage: (clusterCount: number, indexCount: number) => + i18n.translate('xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTooltip', { + defaultMessage: + 'This cluster is using {clusterCount} deprecated cluster settings and {indexCount} deprecated index settings', + values: { + clusterCount, + indexCount, + }, + }), +}; + +export const ESDeprecationStats: FunctionComponent = () => { + const history = useHistory(); + const { api } = useAppContext(); + + const { data: esDeprecations, isLoading, error } = api.useLoadUpgradeStatus(); + + const allDeprecations = esDeprecations?.cluster?.concat(esDeprecations?.indices) ?? []; + const warningDeprecations = allDeprecations.filter( + (deprecation) => deprecation.level === 'warning' + ); + const criticalDeprecations = allDeprecations.filter( + (deprecation) => deprecation.level === 'critical' + ); + + const hasWarnings = warningDeprecations.length > 0; + const hasCritical = criticalDeprecations.length > 0; + const hasNoDeprecations = !isLoading && !error && !hasWarnings && !hasCritical; + const shouldRenderStat = (forSection: boolean) => error || isLoading || forSection; + + return ( + + {i18nTexts.statsTitle} + {error && } + + } + {...(!hasNoDeprecations && reactRouterNavigate(history, '/es_deprecations/cluster'))} + > + + + {hasNoDeprecations && ( + + + + )} + + {shouldRenderStat(hasCritical) && ( + + + {error === null && ( + +

+ {isLoading + ? i18nTexts.loadingText + : i18nTexts.getCriticalDeprecationsMessage(criticalDeprecations.length)} +

+
+ )} +
+
+ )} + + {shouldRenderStat(hasWarnings) && ( + + + {!error && ( + +

+ {isLoading + ? i18nTexts.loadingText + : i18nTexts.getWarningDeprecationMessage( + esDeprecations?.cluster.length ?? 0, + esDeprecations?.indices.length ?? 0 + )} +

+
+ )} +
+
+ )} +
+
+ ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/es_stats_error.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats_error.tsx similarity index 80% rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/es_stats_error.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats_error.tsx index dda7d16599e0c..5db5b80cc42eb 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/es_stats_error.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats_error.tsx @@ -7,9 +7,9 @@ import React from 'react'; -import { EuiIconTip, EuiSpacer } from '@elastic/eui'; -import { ResponseError } from '../../lib/api'; -import { getEsDeprecationError } from '../../lib/es_deprecation_errors'; +import { EuiIconTip } from '@elastic/eui'; +import { ResponseError } from '../../../../lib/api'; +import { getEsDeprecationError } from '../../../../lib/es_deprecation_errors'; interface Props { error: ResponseError; @@ -26,7 +26,7 @@ export const EsStatsErrors: React.FunctionComponent = ({ error }) => { = ({ error }) => { = ({ error }) => { = ({ error }) => { ); } - return ( - <> - - {iconContent} - - ); + return {iconContent}; }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/index.ts new file mode 100644 index 0000000000000..daf2644c2477b --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { ESDeprecationStats } from './es_stats'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/index.ts new file mode 100644 index 0000000000000..231b8ba2d7774 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getReviewLogsStep } from './review_logs_step'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/index.ts new file mode 100644 index 0000000000000..185ec5f2540c4 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { KibanaDeprecationStats } from './kibana_stats'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/kibana_stats.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/kibana_stats.tsx new file mode 100644 index 0000000000000..d7b820aa4a484 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/kibana_stats/kibana_stats.tsx @@ -0,0 +1,188 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent, useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { + EuiCard, + EuiStat, + EuiSpacer, + EuiFlexGroup, + EuiFlexItem, + EuiIconTip, + EuiScreenReaderOnly, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import type { DomainDeprecationDetails } from 'kibana/public'; +import { reactRouterNavigate } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { getDeprecationsUpperLimit } from '../../../../lib/utils'; +import { useAppContext } from '../../../../app_context'; +import { NoDeprecations } from '../no_deprecations'; + +const i18nTexts = { + statsTitle: i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.statsTitle', { + defaultMessage: 'Kibana', + }), + warningDeprecationsTitle: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecationStats.warningDeprecationsTitle', + { + defaultMessage: 'Warning', + } + ), + criticalDeprecationsTitle: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecationStats.criticalDeprecationsTitle', + { + defaultMessage: 'Critical', + } + ), + loadingError: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecationStats.loadingErrorMessage', + { + defaultMessage: 'An error occurred while retrieving Kibana deprecations.', + } + ), + loadingText: i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.loadingText', { + defaultMessage: 'Loading Kibana deprecation stats…', + }), + getCriticalDeprecationsMessage: (criticalDeprecations: number) => + i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.criticalDeprecationsLabel', { + defaultMessage: + 'Kibana has {criticalDeprecations} critical {criticalDeprecations, plural, one {deprecation} other {deprecations}}', + values: { + criticalDeprecations, + }, + }), + getWarningDeprecationsMessage: (warningDeprecations: number) => + i18n.translate('xpack.upgradeAssistant.kibanaDeprecationStats.getWarningDeprecationsMessage', { + defaultMessage: + 'Kibana has {warningDeprecations} warning {warningDeprecations, plural, one {deprecation} other {deprecations}}', + values: { + warningDeprecations, + }, + }), +}; + +export const KibanaDeprecationStats: FunctionComponent = () => { + const history = useHistory(); + const { deprecations } = useAppContext(); + + const [kibanaDeprecations, setKibanaDeprecations] = useState< + DomainDeprecationDetails[] | undefined + >(undefined); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(undefined); + + useEffect(() => { + async function getAllDeprecations() { + setIsLoading(true); + + try { + const response = await deprecations.getAllDeprecations(); + setKibanaDeprecations(response); + } catch (e) { + setError(e); + } + + setIsLoading(false); + } + + getAllDeprecations(); + }, [deprecations]); + + const warningDeprecationsCount = + kibanaDeprecations?.filter((deprecation) => deprecation.level === 'warning')?.length ?? 0; + const criticalDeprecationsCount = + kibanaDeprecations?.filter((deprecation) => deprecation.level === 'critical')?.length ?? 0; + + const hasCritical = criticalDeprecationsCount > 0; + const hasWarnings = warningDeprecationsCount > 0; + const hasNoDeprecations = !isLoading && !error && !hasWarnings && !hasCritical; + const shouldRenderStat = (forSection: boolean) => error || isLoading || forSection; + + return ( + + {i18nTexts.statsTitle} + {error && ( + + )} + + } + {...(!hasNoDeprecations && reactRouterNavigate(history, '/kibana_deprecations'))} + > + + + {hasNoDeprecations && ( + + + + )} + + {shouldRenderStat(hasCritical) && ( + + + {error === undefined && ( + +

+ {isLoading + ? i18nTexts.loadingText + : i18nTexts.getCriticalDeprecationsMessage(criticalDeprecationsCount)} +

+
+ )} +
+
+ )} + + {shouldRenderStat(hasWarnings) && ( + + + {!error && ( + +

+ {isLoading + ? i18nTexts.loadingText + : i18nTexts.getWarningDeprecationsMessage(warningDeprecationsCount)} +

+
+ )} +
+
+ )} +
+
+ ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/_no_deprecations.scss b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/_no_deprecations.scss new file mode 100644 index 0000000000000..0697efbd6ee37 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/_no_deprecations.scss @@ -0,0 +1,3 @@ +.upgRenderSuccessMessage { + margin-top: $euiSizeL; +} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/index.ts new file mode 100644 index 0000000000000..a2684505eb9c6 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { NoDeprecations } from './no_deprecations'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/no_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/no_deprecations.tsx new file mode 100644 index 0000000000000..06fea677aa0a5 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/no_deprecations/no_deprecations.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent } from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +const i18nTexts = { + noDeprecationsText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecationStats.noDeprecationsText', + { + defaultMessage: 'No warnings. Good to go!', + } + ), +}; + +export const NoDeprecations: FunctionComponent = () => { + return ( + + + + + + + {i18nTexts.noDeprecationsText} + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/review_logs_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/review_logs_step.tsx new file mode 100644 index 0000000000000..2da31a3801dd0 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/review_logs_step.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiText, EuiFlexItem, EuiFlexGroup, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; +import { ESDeprecationStats } from './es_stats'; +import { KibanaDeprecationStats } from './kibana_stats'; + +const i18nTexts = { + reviewStepTitle: i18n.translate('xpack.upgradeAssistant.overview.reviewStepTitle', { + defaultMessage: 'Review deprecated settings and resolve issues', + }), +}; + +export const getReviewLogsStep = ({ currentMajor }: { currentMajor: number }): EuiStepProps => { + return { + title: i18nTexts.reviewStepTitle, + status: 'incomplete', + children: ( + <> + +

+ +

+
+ + + + + + + + + + + + + + ), + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/index.ts new file mode 100644 index 0000000000000..5f74bed47c347 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getUpgradeStep } from './upgrade_step'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx new file mode 100644 index 0000000000000..9b66a28e81cd8 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { + EuiText, + EuiFlexItem, + EuiFlexGroup, + EuiSpacer, + EuiButton, + EuiButtonEmpty, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; +import type { DocLinksStart } from 'src/core/public'; +import { useKibana } from '../../../../shared_imports'; + +const i18nTexts = { + upgradeStepTitle: (currentMajor: number) => + i18n.translate('xpack.upgradeAssistant.overview.upgradeStepTitle', { + defaultMessage: 'Install {currentMajor}.0', + values: { currentMajor }, + }), + upgradeStepDescription: i18n.translate('xpack.upgradeAssistant.overview.upgradeStepDescription', { + defaultMessage: + "Once you've resolved all critical issues and verified that your applications are ready, you can upgrade the Elastic Stack.", + }), + upgradeStepDescriptionForCloud: i18n.translate( + 'xpack.upgradeAssistant.overview.upgradeStepDescriptionForCloud', + { + defaultMessage: + "Once you've resolved all critical issues and verified that your applications are ready, you can upgrade the Elastic Stack. Upgrade your deployment on Elastic Cloud.", + } + ), + upgradeStepLink: i18n.translate('xpack.upgradeAssistant.overview.upgradeStepLink', { + defaultMessage: 'Learn more', + }), + upgradeStepCloudLink: i18n.translate('xpack.upgradeAssistant.overview.upgradeStepCloudLink', { + defaultMessage: 'Upgrade on Cloud', + }), + upgradeGuideLink: i18n.translate('xpack.upgradeAssistant.overview.upgradeGuideLink', { + defaultMessage: 'View upgrade guide', + }), +}; + +const UpgradeStep = ({ docLinks }: { docLinks: DocLinksStart }) => { + const { cloud } = useKibana().services; + + const isCloudEnabled: boolean = Boolean(cloud?.isCloudEnabled); + const cloudDeploymentUrl: string = `${cloud?.baseUrl ?? ''}/deployments/${cloud?.cloudId ?? ''}`; + + let callToAction; + + if (isCloudEnabled) { + callToAction = ( + + + + {i18nTexts.upgradeStepCloudLink} + + + + + + {i18nTexts.upgradeGuideLink} + + + + ); + } else { + callToAction = ( + + {i18nTexts.upgradeStepLink} + + ); + } + + return ( + <> + +

+ {isCloudEnabled + ? i18nTexts.upgradeStepDescriptionForCloud + : i18nTexts.upgradeStepDescription} +

+
+ + + + {callToAction} + + ); +}; + +interface Props { + docLinks: DocLinksStart; + currentMajor: number; +} + +export const getUpgradeStep = ({ docLinks, currentMajor }: Props): EuiStepProps => { + return { + title: i18nTexts.upgradeStepTitle(currentMajor), + status: 'incomplete', + children: , + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/types.ts b/x-pack/plugins/upgrade_assistant/public/application/components/types.ts index e3ba609026a8a..b4fd78252b2ff 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/types.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/types.ts @@ -46,3 +46,14 @@ export enum TelemetryState { } export type EsTabs = 'cluster' | 'indices'; + +export interface DeprecationLoggingPreviewProps { + isDeprecationLogIndexingEnabled: boolean; + onlyDeprecationLogWritingEnabled: boolean; + isLoading: boolean; + isUpdating: boolean; + fetchError: ResponseError | null; + updateError: ResponseError | undefined; + resendRequest: () => void; + toggleLogging: () => void; +} diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts index 063b53fecb983..1b22d26ea7218 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts @@ -63,7 +63,10 @@ export class ApiService { } public useLoadDeprecationLogging() { - return this.useRequest<{ isEnabled: boolean }>({ + return this.useRequest<{ + isDeprecationLogIndexingEnabled: boolean; + isDeprecationLoggingEnabled: boolean; + }>({ path: `${API_BASE_PATH}/deprecation_logging`, method: 'get', }); diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts index deee019f2d132..83fc9cabbbecc 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { validateRegExpString } from './utils'; +import { DEPRECATION_WARNING_UPPER_LIMIT } from '../../../common/constants'; +import { validateRegExpString, getDeprecationsUpperLimit } from './utils'; describe('validRegExpString', () => { it('correctly returns false for invalid strings', () => { @@ -20,3 +21,17 @@ describe('validRegExpString', () => { expect(validateRegExpString('')).toBe(''); }); }); + +describe('getDeprecationsUpperLimit', () => { + it('correctly returns capped number if it goes above limit', () => { + expect(getDeprecationsUpperLimit(1000000)).toBe(`${DEPRECATION_WARNING_UPPER_LIMIT}+`); + expect(getDeprecationsUpperLimit(2000000)).toBe(`${DEPRECATION_WARNING_UPPER_LIMIT}+`); + }); + + it('correctly returns true for valid strings', () => { + expect(getDeprecationsUpperLimit(10)).toBe('10'); + expect(getDeprecationsUpperLimit(DEPRECATION_WARNING_UPPER_LIMIT)).toBe( + DEPRECATION_WARNING_UPPER_LIMIT.toString() + ); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts index b7fe435cb2547..b90038e1166ab 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts @@ -8,6 +8,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { tryCatch, fold } from 'fp-ts/lib/Either'; +import { DEPRECATION_WARNING_UPPER_LIMIT } from '../../../common/constants'; + export const validateRegExpString = (s: string) => pipe( tryCatch( @@ -19,3 +21,16 @@ export const validateRegExpString = (s: string) => () => '' ) ); + +/* + * There isnt much difference between having 1M or 1.1M deprecation warnings, the number is + * so big it beats the purpose of having a little preview of the count. With this we can also + * prevent the container of the value to grow due to the value being so large. + */ +export const getDeprecationsUpperLimit = (count: number) => { + if (count > DEPRECATION_WARNING_UPPER_LIMIT) { + return `${DEPRECATION_WARNING_UPPER_LIMIT}+`; + } + + return count.toString(); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts b/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts index 8cd9f8b6591e3..9fa52e90c9d0e 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts @@ -11,12 +11,14 @@ import { renderApp } from './render_app'; import { KibanaVersionContext } from './app_context'; import { apiService } from './lib/api'; import { breadcrumbService } from './lib/breadcrumbs'; +import { AppServicesContext } from '../types'; export async function mountManagementSection( coreSetup: CoreSetup, params: ManagementAppMountParams, kibanaVersionInfo: KibanaVersionContext, - readonly: boolean + readonly: boolean, + services: AppServicesContext ) { const [ { i18n, docLinks, notifications, application, deprecations }, @@ -41,5 +43,7 @@ export async function mountManagementSection( breadcrumbs: breadcrumbService, getUrlForApp: application.getUrlForApp, deprecations, + application, + services, }); } diff --git a/x-pack/plugins/upgrade_assistant/public/plugin.ts b/x-pack/plugins/upgrade_assistant/public/plugin.ts index 4cffd40faf380..558deffe43d94 100644 --- a/x-pack/plugins/upgrade_assistant/public/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/public/plugin.ts @@ -9,17 +9,13 @@ import SemVer from 'semver/classes/semver'; import { i18n } from '@kbn/i18n'; import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; - +import { SetupDependencies, StartDependencies, AppServicesContext } from './types'; import { Config } from '../common/config'; -interface Dependencies { - management: ManagementSetup; -} - -export class UpgradeAssistantUIPlugin implements Plugin { +export class UpgradeAssistantUIPlugin + implements Plugin { constructor(private ctx: PluginInitializerContext) {} - setup(coreSetup: CoreSetup, { management }: Dependencies) { + setup(coreSetup: CoreSetup, { management, cloud }: SetupDependencies) { const { enabled, readonly } = this.ctx.config.get(); if (!enabled) { @@ -45,7 +41,8 @@ export class UpgradeAssistantUIPlugin implements Plugin { title: pluginName, order: 1, async mount(params) { - const [coreStart] = await coreSetup.getStartServices(); + const [coreStart, { discover, data }] = await coreSetup.getStartServices(); + const services: AppServicesContext = { discover, data, cloud }; const { chrome: { docTitle }, @@ -58,7 +55,8 @@ export class UpgradeAssistantUIPlugin implements Plugin { coreSetup, params, kibanaVersionInfo, - readonly + readonly, + services ); return () => { diff --git a/x-pack/plugins/upgrade_assistant/public/shared_imports.ts b/x-pack/plugins/upgrade_assistant/public/shared_imports.ts index 9007fdc5db04d..c3ffd44662ec2 100644 --- a/x-pack/plugins/upgrade_assistant/public/shared_imports.ts +++ b/x-pack/plugins/upgrade_assistant/public/shared_imports.ts @@ -5,6 +5,9 @@ * 2.0. */ +import { useKibana as _useKibana } from '../../../../src/plugins/kibana_react/public'; +import { AppServicesContext } from './types'; + export { sendRequest, SendRequestConfig, @@ -13,3 +16,9 @@ export { UseRequestConfig, SectionLoading, } from '../../../../src/plugins/es_ui_shared/public/'; + +export { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public'; + +export { DataPublicPluginStart } from '../../../../src/plugins/data/public'; + +export const useKibana = () => _useKibana(); diff --git a/x-pack/plugins/upgrade_assistant/public/types.ts b/x-pack/plugins/upgrade_assistant/public/types.ts new file mode 100644 index 0000000000000..a2b49305c32d4 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DiscoverStart } from 'src/plugins/discover/public'; +import { ManagementSetup } from 'src/plugins/management/public'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { CloudSetup } from '../../cloud/public'; +import { LicensingPluginStart } from '../../licensing/public'; + +export interface AppServicesContext { + cloud?: CloudSetup; + discover: DiscoverStart; + data: DataPublicPluginStart; +} + +export interface SetupDependencies { + management: ManagementSetup; + cloud?: CloudSetup; +} +export interface StartDependencies { + licensing: LicensingPluginStart; + discover: DiscoverStart; + data: DataPublicPluginStart; +} diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts index b9595d2b0bb64..0e01d8d6a3458 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts @@ -10,6 +10,7 @@ import { getDeprecationLoggingStatus, isDeprecationLoggingEnabled, setDeprecationLogging, + isDeprecationLogIndexingEnabled, } from './es_deprecation_logging_apis'; describe('getDeprecationLoggingStatus', () => { @@ -28,7 +29,16 @@ describe('setDeprecationLogging', () => { const dataClient = elasticsearchServiceMock.createScopedClusterClient(); await setDeprecationLogging(dataClient, true); expect(dataClient.asCurrentUser.cluster.putSettings).toHaveBeenCalledWith({ - body: { transient: { 'logger.deprecation': 'WARN' } }, + body: { + persistent: { + 'logger.deprecation': 'WARN', + 'cluster.deprecation_indexing.enabled': true, + }, + transient: { + 'logger.deprecation': 'WARN', + 'cluster.deprecation_indexing.enabled': true, + }, + }, }); }); }); @@ -38,7 +48,16 @@ describe('setDeprecationLogging', () => { const dataClient = elasticsearchServiceMock.createScopedClusterClient(); await setDeprecationLogging(dataClient, false); expect(dataClient.asCurrentUser.cluster.putSettings).toHaveBeenCalledWith({ - body: { transient: { 'logger.deprecation': 'ERROR' } }, + body: { + persistent: { + 'logger.deprecation': 'ERROR', + 'cluster.deprecation_indexing.enabled': false, + }, + transient: { + 'logger.deprecation': 'ERROR', + 'cluster.deprecation_indexing.enabled': false, + }, + }, }); }); }); @@ -84,3 +103,24 @@ describe('isDeprecationLoggingEnabled', () => { ).toBe(true); }); }); + +describe('isDeprecationLogIndexingEnabled', () => { + it('allows transient to override persistent and default', () => { + expect( + isDeprecationLogIndexingEnabled({ + default: { cluster: { deprecation_indexing: { enabled: 'false' } } }, + persistent: { cluster: { deprecation_indexing: { enabled: 'false' } } }, + transient: { cluster: { deprecation_indexing: { enabled: 'true' } } }, + }) + ).toBe(true); + }); + + it('allows persistent to override default', () => { + expect( + isDeprecationLogIndexingEnabled({ + default: { cluster: { deprecation_indexing: { enabled: 'false' } } }, + persistent: { cluster: { deprecation_indexing: { enabled: 'true' } } }, + }) + ).toBe(true); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts index 5b1900b881d45..214aabb989921 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts @@ -7,10 +7,7 @@ import { get } from 'lodash'; import { IScopedClusterClient } from 'src/core/server'; - -interface DeprecationLoggingStatus { - isEnabled: boolean; -} +import { DeprecationLoggingStatus } from '../../common/types'; export async function getDeprecationLoggingStatus( dataClient: IScopedClusterClient @@ -20,7 +17,8 @@ export async function getDeprecationLoggingStatus( }); return { - isEnabled: isDeprecationLoggingEnabled(response), + isDeprecationLogIndexingEnabled: isDeprecationLogIndexingEnabled(response), + isDeprecationLoggingEnabled: isDeprecationLoggingEnabled(response), }; } @@ -30,17 +28,38 @@ export async function setDeprecationLogging( ): Promise { const { body: response } = await dataClient.asCurrentUser.cluster.putSettings({ body: { + persistent: { + 'logger.deprecation': isEnabled ? 'WARN' : 'ERROR', + 'cluster.deprecation_indexing.enabled': isEnabled, + }, + /* + * If we only set the persistent setting, we can end up in a situation in which a user has + * set transient on/off. And when toggling and reloading the page the transient setting will + * have priority over it thus "overriding" whatever the user selected. + */ transient: { 'logger.deprecation': isEnabled ? 'WARN' : 'ERROR', + 'cluster.deprecation_indexing.enabled': isEnabled, }, }, }); return { - isEnabled: isDeprecationLoggingEnabled(response), + isDeprecationLogIndexingEnabled: isEnabled, + isDeprecationLoggingEnabled: isDeprecationLoggingEnabled(response), }; } +export function isDeprecationLogIndexingEnabled(settings: any) { + const clusterDeprecationLoggingEnabled = ['default', 'persistent', 'transient'].reduce( + (currentLogLevel, settingsTier) => + get(settings, [settingsTier, 'cluster', 'deprecation_indexing', 'enabled'], currentLogLevel), + 'false' + ); + + return clusterDeprecationLoggingEnabled === 'true'; +} + export function isDeprecationLoggingEnabled(settings: any) { const deprecationLogLevel = ['default', 'persistent', 'transient'].reduce( (currentLogLevel, settingsTier) => diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts index 30195f6652fb2..2227139c53cda 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts @@ -31,6 +31,11 @@ describe('Upgrade Assistant Usage Collector', () => { logger: { deprecation: 'WARN', }, + cluster: { + deprecation_indexing: { + enabled: 'true', + }, + }, }, }, }); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts index 564cd69c042b8..a6253ab1091da 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts @@ -20,7 +20,10 @@ import { UpgradeAssistantTelemetrySavedObject, UpgradeAssistantTelemetrySavedObjectAttributes, } from '../../../common/types'; -import { isDeprecationLoggingEnabled } from '../es_deprecation_logging_apis'; +import { + isDeprecationLogIndexingEnabled, + isDeprecationLoggingEnabled, +} from '../es_deprecation_logging_apis'; async function getSavedObjectAttributesFromRepo( savedObjectsRepository: ISavedObjectsRepository, @@ -45,7 +48,10 @@ async function getDeprecationLoggingStatusValue(esClient: ElasticsearchClient): include_defaults: true, }); - return isDeprecationLoggingEnabled(loggerDeprecationCallResult); + return ( + isDeprecationLogIndexingEnabled(loggerDeprecationCallResult) && + isDeprecationLoggingEnabled(loggerDeprecationCallResult) + ); } catch (e) { return false; } diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts index 8fb677feabd1b..870bd6b985661 100644 --- a/x-pack/plugins/upgrade_assistant/server/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts @@ -16,6 +16,7 @@ import { SavedObjectsClient, SavedObjectsServiceStart, } from '../../../../src/core/server'; +import { InfraPluginSetup } from '../../infra/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; @@ -31,6 +32,7 @@ import { reindexOperationSavedObjectType, mlSavedObjectType, } from './saved_object_types'; +import { DEPRECATION_LOGS_SOURCE_ID, DEPRECATION_LOGS_INDEX_PATTERN } from '../common/constants'; import { RouteDependencies } from './types'; @@ -38,6 +40,7 @@ interface PluginsSetup { usageCollection: UsageCollectionSetup; licensing: LicensingPluginSetup; features: FeaturesPluginSetup; + infra: InfraPluginSetup; } export class UpgradeAssistantServerPlugin implements Plugin { @@ -66,8 +69,8 @@ export class UpgradeAssistantServerPlugin implements Plugin { } setup( - { http, getStartServices, capabilities, savedObjects }: CoreSetup, - { usageCollection, features, licensing }: PluginsSetup + { http, getStartServices, savedObjects }: CoreSetup, + { usageCollection, features, licensing, infra }: PluginsSetup ) { this.licensing = licensing; @@ -88,6 +91,21 @@ export class UpgradeAssistantServerPlugin implements Plugin { ], }); + // We need to initialize the deprecation logs plugin so that we can + // navigate from this app to the observability app using a source_id. + infra.defineInternalSourceConfiguration(DEPRECATION_LOGS_SOURCE_ID, { + name: 'deprecationLogs', + description: 'deprecation logs', + logIndices: { + type: 'index_name', + indexName: DEPRECATION_LOGS_INDEX_PATTERN, + }, + logColumns: [ + { timestampColumn: { id: 'timestampField' } }, + { messageColumn: { id: 'messageField' } }, + ], + }); + const router = http.createRouter(); const dependencies: RouteDependencies = { diff --git a/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts index 0b595df0dc8c4..e146415b1ccfc 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts @@ -37,18 +37,26 @@ describe('deprecation logging API', () => { }); describe('GET /api/upgrade_assistant/deprecation_logging', () => { - it('returns isEnabled', async () => { + it('returns that indexing and writing logs is enabled', async () => { (routeHandlerContextMock.core.elasticsearch.client.asCurrentUser.cluster .getSettings as jest.Mock).mockResolvedValue({ - body: { default: { logger: { deprecation: 'WARN' } } }, + body: { + default: { + cluster: { deprecation_indexing: { enabled: 'true' } }, + }, + }, }); + const resp = await routeDependencies.router.getHandler({ method: 'get', pathPattern: '/api/upgrade_assistant/deprecation_logging', })(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory); expect(resp.status).toEqual(200); - expect(resp.payload).toEqual({ isEnabled: true }); + expect(resp.payload).toEqual({ + isDeprecationLogIndexingEnabled: true, + isDeprecationLoggingEnabled: true, + }); }); it('returns an error if it throws', async () => { @@ -64,17 +72,26 @@ describe('deprecation logging API', () => { }); describe('PUT /api/upgrade_assistant/deprecation_logging', () => { - it('returns isEnabled', async () => { + it('returns that indexing and writing logs is enabled', async () => { (routeHandlerContextMock.core.elasticsearch.client.asCurrentUser.cluster .putSettings as jest.Mock).mockResolvedValue({ - body: { default: { logger: { deprecation: 'ERROR' } } }, + body: { + default: { + logger: { deprecation: 'WARN' }, + cluster: { deprecation_indexing: { enabled: 'true' } }, + }, + }, }); + const resp = await routeDependencies.router.getHandler({ method: 'put', pathPattern: '/api/upgrade_assistant/deprecation_logging', })(routeHandlerContextMock, { body: { isEnabled: true } }, kibanaResponseFactory); - expect(resp.payload).toEqual({ isEnabled: false }); + expect(resp.payload).toEqual({ + isDeprecationLogIndexingEnabled: true, + isDeprecationLoggingEnabled: true, + }); }); it('returns an error if it throws', async () => { diff --git a/x-pack/plugins/upgrade_assistant/tsconfig.json b/x-pack/plugins/upgrade_assistant/tsconfig.json index 33a1421fbb0c1..39d7404ebea9d 100644 --- a/x-pack/plugins/upgrade_assistant/tsconfig.json +++ b/x-pack/plugins/upgrade_assistant/tsconfig.json @@ -22,5 +22,7 @@ { "path": "../features/tsconfig.json" }, { "path": "../licensing/tsconfig.json" }, { "path": "../../../src/plugins/es_ui_shared/tsconfig.json" }, + { "path": "../infra/tsconfig.json" }, + { "path": "../cloud/tsconfig.json" }, ] } From 7a1ece68a2d247042c02ee44f669cfbc53f92069 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Wed, 18 Aug 2021 03:49:06 -0400 Subject: [PATCH 02/11] [Security Solution][Endpoint][TrustedApps] Update trusted apps ui (#108583) --- .../__snapshots__/index.test.tsx.snap | 4 +- .../components/item_details_card/index.tsx | 15 +- .../__snapshots__/index.test.tsx.snap | 109 - .../control_panel/index.stories.tsx | 40 - .../components/control_panel/index.test.tsx | 59 - .../view/components/control_panel/index.tsx | 42 - .../view/components/empty_state.tsx | 2 +- .../__snapshots__/index.test.tsx.snap | 36 +- .../components/trusted_app_card/index.tsx | 25 +- .../__snapshots__/index.test.tsx.snap | 822 +- .../__snapshots__/index.test.tsx.snap | 12926 ---------------- .../trusted_apps_list/index.stories.tsx | 94 - .../trusted_apps_list/index.test.tsx | 164 - .../components/trusted_apps_list/index.tsx | 218 - .../pages/trusted_apps/view/translations.ts | 10 +- .../view/trusted_apps_notifications.tsx | 38 +- .../view/trusted_apps_page.test.tsx | 104 +- .../trusted_apps/view/trusted_apps_page.tsx | 34 +- .../translations/translations/zh-CN.json | 1 - .../apps/endpoint/trusted_apps_list.ts | 2 +- 20 files changed, 541 insertions(+), 14204 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/__snapshots__/index.test.tsx.snap delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.stories.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.stories.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap index f0fd8427140df..4976c859c00e9 100644 --- a/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap @@ -181,7 +181,9 @@ exports[`item_details_card ItemDetailsCard should render correctly with no actio exports[`item_details_card ItemDetailsPropertySummary should render correctly 1`] = ` - + name 1 theme.eui.euiSize}; + &&& { + margin-left: 0; + } + padding: ${({ theme }) => theme.eui.euiSizeM} ${({ theme }) => theme.eui.euiSizeL} + ${({ theme }) => theme.eui.euiSizeL} 0; + .trustedAppsConditionsTable { + margin-left: ${({ theme }) => theme.eui.euiSize}; + } `; const DescriptionListTitle = styled(EuiDescriptionListTitle)` &&& { width: 40%; + margin-top: 0; + margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; } `; const DescriptionListDescription = styled(EuiDescriptionListDescription)` &&& { width: 60%; + margin-top: 0; + margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; } `; @@ -80,7 +91,7 @@ interface ItemDetailsPropertySummaryProps { export const ItemDetailsPropertySummary = memo( ({ name, value }) => ( <> - {name} + {name} {value} ) diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 190b78761a9de..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,109 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`control_panel ControlPanel should render grid selection correctly 1`] = ` - - - - 0 trusted applications - - - - - - -`; - -exports[`control_panel ControlPanel should render list selection correctly 1`] = ` - - - - 0 trusted applications - - - - - - -`; - -exports[`control_panel ControlPanel should render plural count correctly 1`] = ` - - - - 100 trusted applications - - - - - - -`; - -exports[`control_panel ControlPanel should render singular count correctly 1`] = ` - - - - 1 trusted application - - - - - - -`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.stories.tsx deleted file mode 100644 index 341017dd6d718..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.stories.tsx +++ /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. - */ - -import React, { useState } from 'react'; -import { ThemeProvider } from 'styled-components'; -import { storiesOf, addDecorator } from '@storybook/react'; -import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; - -import { ControlPanel, ControlPanelProps } from '.'; -import { ViewType } from '../../../state'; - -addDecorator((storyFn) => ( - ({ eui: euiLightVars, darkMode: false })}>{storyFn()} -)); - -const useRenderStory = (props: Omit) => { - const [selectedOption, setSelectedOption] = useState(props.currentViewType); - - return ( - - ); -}; - -storiesOf('TrustedApps/ControlPanel', module) - .add('list view selected', () => { - return useRenderStory({ totalItemCount: 0, currentViewType: 'list' }); - }) - .add('plural totals', () => { - return useRenderStory({ totalItemCount: 200, currentViewType: 'grid' }); - }) - .add('singular totals', () => { - return useRenderStory({ totalItemCount: 1, currentViewType: 'grid' }); - }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.test.tsx deleted file mode 100644 index 5530e15f981e9..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.test.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render } from '@testing-library/react'; -import { shallow } from 'enzyme'; -import React from 'react'; - -import { ControlPanel } from '.'; - -describe('control_panel', () => { - describe('ControlPanel', () => { - it('should render grid selection correctly', () => { - const element = shallow( - {}} /> - ); - - expect(element).toMatchSnapshot(); - }); - - it('should render list selection correctly', () => { - const element = shallow( - {}} /> - ); - - expect(element).toMatchSnapshot(); - }); - - it('should render singular count correctly', () => { - const element = shallow( - {}} /> - ); - - expect(element).toMatchSnapshot(); - }); - - it('should render plural count correctly', () => { - const element = shallow( - {}} /> - ); - - expect(element).toMatchSnapshot(); - }); - - it('should trigger onViewTypeChange', async () => { - const onToggle = jest.fn(); - const element = render( - - ); - - (await element.findAllByTestId('viewTypeToggleButton'))[0].click(); - - expect(onToggle).toBeCalledWith('grid'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.tsx deleted file mode 100644 index 89e9a8997f565..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/control_panel/index.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { ViewType } from '../../../state'; -import { ViewTypeToggle } from '../view_type_toggle'; - -export interface ControlPanelProps { - totalItemCount: number; - currentViewType: ViewType; - onViewTypeChange: (value: ViewType) => void; -} - -export const ControlPanel = memo( - ({ totalItemCount, currentViewType, onViewTypeChange }) => { - return ( - - - - {i18n.translate('xpack.securitySolution.trustedapps.list.totalCount', { - defaultMessage: - '{totalItemCount, plural, one {# trusted application} other {# trusted applications}}', - values: { totalItemCount }, - })} - - - - - - - ); - } -); - -ControlPanel.displayName = 'ControlPanel'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/empty_state.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/empty_state.tsx index 43e5516750a1d..d4b02b6ac467a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/empty_state.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/empty_state.tsx @@ -41,7 +41,7 @@ export const EmptyState = memo<{ > } diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap index 7439245bc9571..cbeea78f51040 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap @@ -22,40 +22,40 @@ exports[`trusted_app_card TrustedAppCard should render correctly 1`] = ` } /> } /> } /> } /> } @@ -72,6 +72,7 @@ exports[`trusted_app_card TrustedAppCard should render correctly 1`] = ` /> @@ -146,40 +146,40 @@ exports[`trusted_app_card TrustedAppCard should trim long texts 1`] = ` } /> } /> } /> } /> } @@ -196,6 +196,7 @@ exports[`trusted_app_card TrustedAppCard should trim long texts 1`] = ` /> diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx index 2016e43f53c42..419d8aaedfe03 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx @@ -6,6 +6,7 @@ */ import React, { memo, useCallback, useMemo } from 'react'; +import { isEmpty } from 'lodash/fp'; import { EuiTableFieldDataColumnType } from '@elastic/eui'; import { @@ -140,28 +141,30 @@ export const TrustedAppCard = memo( /> } /> - - } - /> + {!isEmpty(trustedApp.description) && ( + + } + /> + )} getEntriesColumnDefinitions(), [])} items={useMemo(() => [...trustedApp.entries], [trustedApp.entries])} badge="and" + className="trustedAppsConditionsTable" responsive /> diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap index 7b3ae2e2b3b27..1bc2581a520ae 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap @@ -378,15 +378,27 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` } .c5 { - padding: 16px; + padding: 12px 24px 24px 0; +} + +.c5.c5.c5 { + margin-left: 0; +} + +.c5 .trustedAppsConditionsTable { + margin-left: 16px; } .c3.c3.c3 { width: 40%; + margin-top: 0; + margin-bottom: 8px; } .c4.c4.c4 { width: 60%; + margin-top: 0; + margin-bottom: 8px; } .c1 { @@ -432,7 +444,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` class="euiDescriptionList euiDescriptionList--column euiDescriptionList--compressed" >
Name
@@ -448,7 +460,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
OS
@@ -464,9 +476,9 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
- Date Created + Date created
- Created By + Created by
- Date Modified + Date modified
- Modified By + Modified by
Description
@@ -550,7 +562,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` class="euiFlexItem euiFlexItem--flexGrow1" >
-
-
-
-
- -
-
- - - -`; - -exports[`TrustedAppsList renders correctly when loaded data 1`] = ` -
-
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - Name - - - - - - OS - - - - - - Date Created - - - - - - Created By - - - - - - Actions - - - - - - -
-
- Name -
-
- - - trusted app 0 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 1 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 2 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 3 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 4 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 5 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 6 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 7 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 8 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 9 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 10 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 11 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 12 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 13 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 14 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 15 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 16 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 17 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 18 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 19 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-`; - -exports[`TrustedAppsList renders correctly when loading data for the first time 1`] = ` -
-
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - - -
-
- - - Name - - - - - - OS - - - - - - Date Created - - - - - - Created By - - - - - - Actions - - - - - - -
-
- - No items found - -
-
-
-
-
-`; - -exports[`TrustedAppsList renders correctly when loading data for the second time 1`] = ` -
-
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - Name - - - - - - OS - - - - - - Date Created - - - - - - Created By - - - - - - Actions - - - - - - -
-
- Name -
-
- - - trusted app 0 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 1 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 2 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 3 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 4 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 5 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 6 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 7 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 8 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 9 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 10 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 11 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 12 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 13 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 14 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 15 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 16 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 17 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 18 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 19 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-`; - -exports[`TrustedAppsList renders correctly when new page and page size set (not loading yet) 1`] = ` -
-
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - Name - - - - - - OS - - - - - - Date Created - - - - - - Created By - - - - - - Actions - - - - - - -
-
- Name -
-
- - - trusted app 0 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 1 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 2 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 3 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 4 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 5 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 6 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 7 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 8 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 9 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 10 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 11 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 12 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 13 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 14 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 15 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 16 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 17 - - -
-
-
- OS -
-
- - - Linux - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 18 - - -
-
-
- OS -
-
- - - Windows - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
- Name -
-
- - - trusted app 19 - - -
-
-
- OS -
-
- - - Mac - - -
-
-
- Date Created -
-
- 1 minute ago -
-
-
- Created By -
-
- - - someone - - -
-
-
- - - - Remove - - -
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.stories.tsx deleted file mode 100644 index 8c7464824158e..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.stories.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { Provider } from 'react-redux'; -import { ThemeProvider } from 'styled-components'; -import { storiesOf } from '@storybook/react'; -import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; - -import { KibanaContextProvider } from '../../../../../../../../../../src/plugins/kibana_react/public'; - -import { - createGlobalNoMiddlewareStore, - createListFailedResourceState, - createListLoadedResourceState, - createListLoadingResourceState, - createTrustedAppsListResourceStateChangedAction, -} from '../../../test_utils'; - -import { TrustedAppsList } from '.'; - -const now = 111111; - -const renderList = (store: ReturnType) => ( - - 'MMM D, YYYY @ HH:mm:ss.SSS' } }}> - ({ eui: euiLightVars, darkMode: false })}> - - - - -); - -storiesOf('TrustedApps/TrustedAppsList', module) - .add('default', () => { - return renderList(createGlobalNoMiddlewareStore()); - }) - .add('loading', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction(createListLoadingResourceState()) - ); - - return renderList(store); - }) - .add('error', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListFailedResourceState('Intenal Server Error') - ) - ); - - return renderList(store); - }) - .add('loaded', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 10 }, now) - ) - ); - - return renderList(store); - }) - .add('loading second time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadingResourceState(createListLoadedResourceState({ pageSize: 10 }, now)) - ) - ); - - return renderList(store); - }) - .add('long texts', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 10 }, now, true) - ) - ); - - return renderList(store); - }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.test.tsx deleted file mode 100644 index 64efda2c90ed1..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.test.tsx +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render } from '@testing-library/react'; -import React from 'react'; -import { Provider } from 'react-redux'; -import { ThemeProvider } from 'styled-components'; - -import { - createSampleTrustedApp, - createListFailedResourceState, - createListLoadedResourceState, - createListLoadingResourceState, - createTrustedAppsListResourceStateChangedAction, - createUserChangedUrlAction, - createGlobalNoMiddlewareStore, -} from '../../../test_utils'; - -import { TrustedAppsList } from '.'; -import { getMockTheme } from '../../../../../../common/lib/kibana/kibana_react.mock'; - -const mockTheme = getMockTheme({ - eui: { - euiColorLightestShade: '#ece', - }, -}); - -jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ - htmlIdGenerator: () => () => 'mockId', -})); - -const now = 111111; - -const renderList = (store: ReturnType) => { - const Wrapper: React.FC = ({ children }) => ( - - {children} - - ); - - return render(, { wrapper: Wrapper }); -}; - -describe('TrustedAppsList', () => { - it('renders correctly initially', () => { - expect(renderList(createGlobalNoMiddlewareStore()).container).toMatchSnapshot(); - }); - - it('renders correctly when loading data for the first time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction(createListLoadingResourceState()) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when failed loading data for the first time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListFailedResourceState('Intenal Server Error') - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when loaded data', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 20 }, now) - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when new page and page size set (not loading yet)', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 20 }, now) - ) - ); - store.dispatch( - createUserChangedUrlAction('/administration/trusted_apps', '?page_index=2&page_size=50') - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when loading data for the second time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadingResourceState(createListLoadedResourceState({ pageSize: 20 }, now)) - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when failed loading data for the second time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListFailedResourceState( - 'Intenal Server Error', - createListLoadedResourceState({ pageSize: 20 }, now) - ) - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when item details expanded', async () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 20 }, now) - ) - ); - - const element = renderList(store); - - (await element.findAllByTestId('trustedAppsListItemExpandButton'))[0].click(); - - expect(element.container).toMatchSnapshot(); - }); - - it('triggers deletion dialog when delete action clicked', async () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 20 }, now) - ) - ); - store.dispatch = jest.fn(); - - (await renderList(store).findAllByTestId('trustedAppDeleteAction'))[0].click(); - - expect(store.dispatch).toBeCalledWith({ - type: 'trustedAppDeletionDialogStarted', - payload: { - entry: createSampleTrustedApp(0), - }, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.tsx deleted file mode 100644 index 5d3b8a86b7a69..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/index.tsx +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, ReactNode, useCallback, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; -import { EuiBasicTable, EuiBasicTableColumn, EuiButtonIcon, RIGHT_ALIGNMENT } from '@elastic/eui'; - -import { useHistory } from 'react-router-dom'; -import { Immutable, TrustedApp } from '../../../../../../../common/endpoint/types'; - -import { - getCurrentLocation, - getListErrorMessage, - getListItems, - getListPagination, - isListLoading, -} from '../../../store/selectors'; - -import { FormattedDate } from '../../../../../../common/components/formatted_date'; -import { TextFieldValue } from '../../../../../../common/components/text_field_value'; - -import { useTrustedAppsNavigateCallback, useTrustedAppsSelector } from '../../hooks'; - -import { ACTIONS_COLUMN_TITLE, LIST_ACTIONS, OS_TITLES, PROPERTY_TITLES } from '../../translations'; -import { TrustedAppCard, TrustedAppCardProps } from '../trusted_app_card'; -import { getTrustedAppsListPath } from '../../../../../common/routing'; - -interface DetailsMap { - [K: string]: ReactNode; -} - -const ExpandedRowContent = memo>(({ trustedApp }) => { - const dispatch = useDispatch(); - const history = useHistory(); - const location = useTrustedAppsSelector(getCurrentLocation); - - const handleOnDelete = useCallback(() => { - dispatch({ - type: 'trustedAppDeletionDialogStarted', - payload: { entry: trustedApp }, - }); - }, [dispatch, trustedApp]); - - const handleOnEdit = useCallback(() => { - history.push( - getTrustedAppsListPath({ - ...location, - show: 'edit', - id: trustedApp.id, - }) - ); - }, [history, location, trustedApp.id]); - - return ( - - ); -}); -ExpandedRowContent.displayName = 'ExpandedRowContent'; - -export const TrustedAppsList = memo(() => { - const dispatch = useDispatch(); - - const [showDetailsFor, setShowDetailsFor] = useState<{ [key: string]: boolean }>({}); - - // Cast below is needed because EuiBasicTable expects listItems to be mutable - const listItems = useTrustedAppsSelector(getListItems) as TrustedApp[]; - const pagination = useTrustedAppsSelector(getListPagination); - const listError = useTrustedAppsSelector(getListErrorMessage); - const isLoading = useTrustedAppsSelector(isListLoading); - - const toggleShowDetailsFor = useCallback((trustedAppId) => { - setShowDetailsFor((prevState) => { - const newState = { ...prevState }; - if (prevState[trustedAppId]) { - delete newState[trustedAppId]; - } else { - newState[trustedAppId] = true; - } - return newState; - }); - }, []); - - const detailsMap = useMemo(() => { - return Object.keys(showDetailsFor).reduce((expandMap, trustedAppId) => { - const trustedApp = listItems.find((ta) => ta.id === trustedAppId); - - if (trustedApp) { - expandMap[trustedAppId] = ; - } - - return expandMap; - }, {}); - }, [listItems, showDetailsFor]); - - const handleTableOnChange = useTrustedAppsNavigateCallback(({ page }) => ({ - page_index: page.index, - page_size: page.size, - })); - - const tableColumns: Array>> = useMemo(() => { - return [ - { - field: 'name', - name: PROPERTY_TITLES.name, - 'data-test-subj': 'trustedAppNameTableCell', - render(value: TrustedApp['name']) { - return ( - - ); - }, - }, - { - field: 'os', - name: PROPERTY_TITLES.os, - render(value: TrustedApp['os']) { - return ( - - ); - }, - }, - { - field: 'created_at', - name: PROPERTY_TITLES.created_at, - render(value: TrustedApp['created_at']) { - return ( - - ); - }, - }, - { - field: 'created_by', - name: PROPERTY_TITLES.created_by, - render(value: TrustedApp['created_by']) { - return ( - - ); - }, - }, - { - name: ACTIONS_COLUMN_TITLE, - actions: [ - { - name: LIST_ACTIONS.delete.name, - description: LIST_ACTIONS.delete.description, - 'data-test-subj': 'trustedAppDeleteAction', - isPrimary: true, - icon: 'trash', - color: 'danger', - type: 'icon', - onClick: (item: Immutable) => { - dispatch({ - type: 'trustedAppDeletionDialogStarted', - payload: { entry: item }, - }); - }, - }, - ], - }, - { - align: RIGHT_ALIGNMENT, - width: '40px', - isExpander: true, - render({ id }: Immutable) { - return ( - toggleShowDetailsFor(id)} - aria-label={detailsMap[id] ? 'Collapse' : 'Expand'} - iconType={detailsMap[id] ? 'arrowUp' : 'arrowDown'} - data-test-subj="trustedAppsListItemExpandButton" - /> - ); - }, - }, - ]; - }, [detailsMap, dispatch, toggleShowDetailsFor]); - - return ( - - ); -}); - -TrustedAppsList.displayName = 'TrustedAppsList'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts index 803e292c58eb5..9e2cad93fc51f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts @@ -19,7 +19,7 @@ export { OS_TITLES } from '../../../common/translations'; export const ABOUT_TRUSTED_APPS = i18n.translate('xpack.securitySolution.trustedapps.aboutInfo', { defaultMessage: 'Add a trusted application to improve performance or alleviate conflicts with other applications running on ' + - 'your hosts. Trusted applications are applied to hosts running the Endpoint Security integration on their agents.', + 'your hosts.', }); export const CONDITION_FIELD_TITLE: { [K in ConditionEntryField]: string } = { @@ -71,16 +71,16 @@ export const PROPERTY_TITLES: Readonly< defaultMessage: 'OS', }), created_at: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.createdAt', { - defaultMessage: 'Date Created', + defaultMessage: 'Date created', }), created_by: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.createdBy', { - defaultMessage: 'Created By', + defaultMessage: 'Created by', }), updated_at: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.updatedAt', { - defaultMessage: 'Date Modified', + defaultMessage: 'Date modified', }), updated_by: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.updatedBy', { - defaultMessage: 'Modified By', + defaultMessage: 'Modified by', }), description: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.description', { defaultMessage: 'Description', diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.tsx index 94fd1a2bb4991..a86a08a894ed9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.tsx @@ -48,23 +48,33 @@ const getDeletionSuccessMessage = (entry: Immutable) => { }; const getCreationSuccessMessage = (entry: Immutable) => { - return i18n.translate( - 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.successToastTitle', - { - defaultMessage: '"{name}" has been added to the Trusted Applications list.', - values: { name: entry.name }, - } - ); + return { + title: i18n.translate('xpack.securitySolution.trustedapps.creationSuccess.title', { + defaultMessage: 'Success!', + }), + text: i18n.translate( + 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.successToastTitle', + { + defaultMessage: '"{name}" has been added to the Trusted Applications list.', + values: { name: entry.name }, + } + ), + }; }; const getUpdateSuccessMessage = (entry: Immutable) => { - return i18n.translate( - 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.updateSuccessToastTitle', - { - defaultMessage: '"{name}" has been updated successfully', - values: { name: entry.name }, - } - ); + return { + title: i18n.translate('xpack.securitySolution.trustedapps.updateSuccess.title', { + defaultMessage: 'Success!', + }), + text: i18n.translate( + 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.updateSuccessToastTitle', + { + defaultMessage: '"{name}" has been updated.', + values: { name: entry.name }, + } + ), + }; }; export const TrustedAppsNotifications = memo(() => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index 2ba357a349b5d..ff7ba8068b4ff 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -46,8 +46,7 @@ const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as j describe('When on the Trusted Apps Page', () => { const expectedAboutInfo = 'Add a trusted application to improve performance or alleviate conflicts with other ' + - 'applications running on your hosts. Trusted applications are applied to hosts running the Endpoint Security ' + - 'integration on their agents.'; + 'applications running on your hosts.'; const generator = new EndpointDocGenerator('policy-list'); @@ -170,7 +169,7 @@ describe('When on the Trusted Apps Page', () => { it('should display a Add Trusted App button', async () => { const { getByTestId } = await renderWithListData(); const addButton = getByTestId('trustedAppsListAddButton'); - expect(addButton.textContent).toBe('Add Trusted Application'); + expect(addButton.textContent).toBe('Add trusted application'); }); it('should display the searchExceptions', async () => { @@ -355,73 +354,6 @@ describe('When on the Trusted Apps Page', () => { }); }); }); - - describe('and the List view is being displayed', () => { - let renderResult: ReturnType; - - const expandFirstRow = () => { - reactTestingLibrary.act(() => { - fireEvent.click(renderResult.getByTestId('trustedAppsListItemExpandButton')); - }); - }; - - beforeEach(async () => { - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps?view_type=list'); - }); - - renderResult = await renderWithListData(); - }); - - it('should display the list', () => { - expect(renderResult.getByTestId('trustedAppsList')); - }); - - it('should show a card when row is expanded', () => { - expandFirstRow(); - expect(renderResult.getByTestId('trustedAppCard')); - }); - - it('should show Edit flyout when edit button on card is clicked', () => { - expandFirstRow(); - reactTestingLibrary.act(() => { - fireEvent.click(renderResult.getByTestId('trustedAppEditButton')); - }); - expect(renderResult.findByTestId('addTrustedAppFlyout')); - }); - - it('should reflect updated information on row and card when updated data is received', async () => { - expandFirstRow(); - reactTestingLibrary.act(() => { - const updatedListContent = createListApiResponse(); - updatedListContent.data[0]!.name = 'updated trusted app'; - updatedListContent.data[0]!.description = 'updated trusted app description'; - - mockedContext.store.dispatch({ - type: 'trustedAppsListResourceStateChanged', - payload: { - newState: { - type: 'LoadedResourceState', - data: { - items: updatedListContent.data, - pageIndex: updatedListContent.page, - pageSize: updatedListContent.per_page, - totalItemsCount: updatedListContent.total, - timestamp: Date.now(), - }, - }, - }, - }); - }); - - // The additional prefix of `Name` is due to the hidden element in DOM that is only shown - // for mobile devices (inserted by the EuiBasicTable) - expect(renderResult.getByTestId('trustedAppNameTableCell').textContent).toEqual( - 'Nameupdated trusted app' - ); - expect(renderResult.getByText('updated trusted app description')); - }); - }); }); describe('and the Add Trusted App button is clicked', () => { @@ -638,9 +570,10 @@ describe('When on the Trusted Apps Page', () => { }); it('should show success toast notification', () => { - expect(coreStart.notifications.toasts.addSuccess.mock.calls[0][0]).toEqual( - '"one app" has been added to the Trusted Applications list.' - ); + expect(coreStart.notifications.toasts.addSuccess.mock.calls[0][0]).toEqual({ + text: '"one app" has been added to the Trusted Applications list.', + title: 'Success!', + }); }); it('should trigger the List to reload', () => { @@ -925,18 +858,7 @@ describe('When on the Trusted Apps Page', () => { describe('and the back button is present', () => { let renderResult: ReturnType; beforeEach(async () => { - renderResult = render(); - await act(async () => { - await waitForAction('trustedAppsListResourceStateChanged'); - }); - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps', { - onBackButtonNavigateTo: [{ appId: 'appId' }], - backButtonLabel: 'back to fleet', - backButtonUrl: '/fleet', - }); - }); - + // Ensure implementation is defined before render to avoid undefined responses from hidden api calls const priorMockImplementation = coreStart.http.get.getMockImplementation(); // @ts-ignore coreStart.http.get.mockImplementation((path, options) => { @@ -957,6 +879,18 @@ describe('When on the Trusted Apps Page', () => { return priorMockImplementation(path); } }); + + renderResult = render(); + await act(async () => { + await waitForAction('trustedAppsListResourceStateChanged'); + }); + reactTestingLibrary.act(() => { + history.push('/administration/trusted_apps', { + onBackButtonNavigateTo: [{ appId: 'appId' }], + backButtonLabel: 'back to fleet', + backButtonUrl: '/fleet', + }); + }); }); it('back button is present', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index a7e86a6703bb5..70698aec509ba 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -15,12 +15,12 @@ import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, - EuiHorizontalRule, EuiLoadingSpinner, EuiSpacer, + EuiText, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; -import { ViewType } from '../state'; import { checkingIfEntriesExist, entriesExist, @@ -32,9 +32,7 @@ import { import { useTrustedAppsNavigateCallback, useTrustedAppsSelector } from './hooks'; import { AdministrationListPage } from '../../../components/administration_list_page'; import { CreateTrustedAppFlyout } from './components/create_trusted_app_flyout'; -import { ControlPanel } from './components/control_panel'; import { TrustedAppsGrid } from './components/trusted_apps_grid'; -import { TrustedAppsList } from './components/trusted_apps_list'; import { TrustedAppDeletionDialog } from './trusted_app_deletion_dialog'; import { TrustedAppsNotifications } from './trusted_apps_notifications'; import { AppAction } from '../../../../common/store/actions'; @@ -73,9 +71,6 @@ export const TrustedAppsPage = memo(() => { show: undefined, id: undefined, })); - const handleViewTypeChange = useTrustedAppsNavigateCallback((viewType: ViewType) => ({ - view_type: viewType, - })); const handleOnSearch = useCallback( (query: string, includedPolicies?: string, excludedPolicies?: string) => { @@ -109,7 +104,7 @@ export const TrustedAppsPage = memo(() => { > ); @@ -144,19 +139,18 @@ export const TrustedAppsPage = memo(() => { > - - - + + {i18n.translate('xpack.securitySolution.trustedapps.list.totalCount', { + defaultMessage: + 'Showing {totalItemsCount, plural, one {# trusted application} other {# trusted applications}}', + values: { totalItemsCount }, + })} + + + - - - {location.view_type === 'grid' && } - {location.view_type === 'list' && } + @@ -172,7 +166,7 @@ export const TrustedAppsPage = memo(() => { title={ } headerBackComponent={backButton} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index fc0613e8b46ea..fa6e840e023b3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -23021,7 +23021,6 @@ "xpack.securitySolution.trustedapps.list.columns.actions": "操作", "xpack.securitySolution.trustedapps.list.pageTitle": "受信任的应用程序", "xpack.securitySolution.trustedapps.list.search.placeholder": "搜索下面的字段:name、description、value", - "xpack.securitySolution.trustedapps.list.totalCount": "{totalItemCount, plural, other {# 个受信任的应用程序}}", "xpack.securitySolution.trustedapps.listEmptyState.message": "当前在您的终端上没有受信任应用程序。", "xpack.securitySolution.trustedapps.listEmptyState.title": "添加您的首个受信任应用程序", "xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.description.hash": "md5、sha1 或 sha256", diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts index e5126a7fbfb63..95fd914d32b07 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts @@ -19,7 +19,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should show page title', async () => { expect(await testSubjects.getVisibleText('header-page-title')).to.equal( - 'Trusted Applications' + 'Trusted applications' ); }); From d810c51aab053dc3961a2bf7461ded631ca9c808 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Wed, 18 Aug 2021 11:27:00 +0300 Subject: [PATCH 03/11] =?UTF-8?q?Failing=20test:=20Chrome=20UI=20Functiona?= =?UTF-8?q?l=20Tests.test/functional/apps/visualize/=5Ftsvb=5Fmarkdown?= =?UTF-8?q?=C2=B7ts=20-=20visualize=20app=20visualize=20ciGroup12=20visual?= =?UTF-8?q?=20builder=20markdown=20should=20render=20mustache=20list=20(#1?= =?UTF-8?q?08651)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/page_objects/visual_builder_page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index f648528b7f615..591cddd18a2b3 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -131,7 +131,7 @@ export class VisualBuilderPageObject extends FtrService { await this.clearMarkdown(); const input = await this.find.byCssSelector('.tvbMarkdownEditor__editor textarea'); await input.type(markdown); - await this.common.sleep(3000); + await this.visChart.waitForVisualizationRenderingStabilized(); } public async clearMarkdown() { From fd1bf565f6d7310313b6d7716929e1ccbb9deb8e Mon Sep 17 00:00:00 2001 From: Kerry Gallagher <471693+Kerry350@users.noreply.github.com> Date: Wed, 18 Aug 2021 10:13:28 +0100 Subject: [PATCH 04/11] [RAC] Update alert status column in alerts table (#108695) * Update alert status in alerts table --- .../pages/alerts/alerts_table_t_grid.tsx | 4 +- .../public/pages/alerts/render_cell_value.tsx | 44 ++++++++++++------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx index d84c820e5d0bd..dd493affac0b5 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx @@ -113,10 +113,10 @@ export const columns: Array< { columnHeaderType: 'not-filtered', displayAsText: i18n.translate('xpack.observability.alertsTGrid.statusColumnDescription', { - defaultMessage: 'Status', + defaultMessage: 'Alert Status', }), id: ALERT_STATUS, - initialWidth: 79, + initialWidth: 110, }, { columnHeaderType: 'not-filtered', diff --git a/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx b/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx index f6e1d41c2a6f9..128f22d707a69 100644 --- a/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiIconTip, EuiLink } from '@elastic/eui'; +import { EuiLink, EuiHealth, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; /** @@ -34,6 +34,7 @@ import { SeverityBadge } from './severity_badge'; import { TopAlert } from '.'; import { parseAlert } from './parse_alert'; import { usePluginContext } from '../../hooks/use_plugin_context'; +import { useTheme } from '../../hooks/use_theme'; const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED; const ALERT_SEVERITY_LEVEL: typeof ALERT_SEVERITY_LEVEL_TYPED = ALERT_SEVERITY_LEVEL_NON_TYPED; @@ -86,24 +87,33 @@ export const getRenderCellValue = ({ } }, [columnId, setCellProps]); + const theme = useTheme(); + switch (columnId) { case ALERT_STATUS: - return value !== 'closed' ? ( - - ) : ( - - ); + switch (value) { + case 'open': + return ( + + {i18n.translate('xpack.observability.alertsTGrid.statusActiveDescription', { + defaultMessage: 'Active', + })} + + ); + case 'closed': + return ( + + + {i18n.translate('xpack.observability.alertsTGrid.statusRecoveredDescription', { + defaultMessage: 'Recovered', + })} + + + ); + default: + // NOTE: This fallback shouldn't be needed. Status should be either "active" or "recovered". + return null; + } case TIMESTAMP: return ; case ALERT_DURATION: From bc7b2f9c59340ae9cf09ee56e18899236fb3c971 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 18 Aug 2021 12:42:30 +0300 Subject: [PATCH 05/11] Switching between some aggregations in bucket section for sibling aggregations breaks the visualization. (#108693) * share between aggs only field and base params: json, label, time shift. * Fix some remarks Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/controls/utils/use_handlers.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts b/src/plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts index bfb0cbc380011..febedd49621fd 100644 --- a/src/plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts +++ b/src/plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts @@ -7,6 +7,7 @@ */ import { useCallback } from 'react'; +import type { SerializableRecord } from '@kbn/utility-types'; import { IAggConfig, AggParamType } from 'src/plugins/data/public'; @@ -20,7 +21,7 @@ function useSubAggParamsHandlers( ) { const setAggParamValue = useCallback( (aggId, paramName, val) => { - const parsedParams = subAgg.toJSON(); + const parsedParams = subAgg.serialize(); const params = { ...parsedParams, params: { @@ -36,10 +37,18 @@ function useSubAggParamsHandlers( const onAggTypeChange = useCallback( (aggId, aggType) => { - const parsedAgg = subAgg.toJSON(); + const parsedAgg = subAgg.serialize(); + const parsedAggParams = parsedAgg.params as SerializableRecord; + // we should share between aggs only field and base params: json, label, time shift. const params = { ...parsedAgg, + params: { + field: parsedAggParams.field, + json: parsedAggParams.json, + customLabel: parsedAggParams.customLabel, + timeShift: parsedAggParams.timeShift, + }, type: aggType, }; From 2592e36c15c96e195208b152881c539d817b3b60 Mon Sep 17 00:00:00 2001 From: Domenico Andreoli Date: Wed, 18 Aug 2021 12:34:52 +0200 Subject: [PATCH 06/11] Realign cypress/ccs_integration with cypress/integration (#109048) cypress/ccs_integration is a subset/fork of cypress/integration and already tends to be left behind. Situation is expected to improve once proper signaling of failing tests is reported to #security-solution-slack-testing. For the moment, let's keep aligning manually. --- .../ccs_integration/detection_alerts/alerts_details.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts index 26f2385d94f1f..3263eb9d70b66 100644 --- a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts @@ -58,7 +58,7 @@ describe('Alert details with unmapped fields', () => { it('Displays the unmapped field on the table', () => { const expectedUnmmappedField = { - row: 88, + row: 90, field: 'unmapped', text: 'This is the unmapped field', }; From e4bf6140fb3829c74fe9d44f14f7e1faaa42cb0a Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 18 Aug 2021 07:37:40 -0400 Subject: [PATCH 07/11] [SECURITY SOLUTION] [RAC] bug actions padding (#109029) * wip * match design for selecting grid view * wip to integrate event rendered view * wip * integration of the event rendered * fix perPage action on Euibasic table * Add bulding block background color to EventRenderedView * styling * remove header * fix types * fix unit tests * use memo for listProps * fix styling + add feature flag * review I * fix merge * change the gutter size * fix bugs * fix alert consumers Co-authored-by: Pablo Neves Machado Co-authored-by: Angela Chuang Co-authored-by: Michael Olorunnisola --- .../cases/components/case_view/index.tsx | 8 ++-- .../components/alerts_viewer/alerts_table.tsx | 5 ++- .../common/components/alerts_viewer/index.tsx | 2 + .../common/components/alerts_viewer/types.ts | 2 + .../common/components/events_viewer/index.tsx | 8 +--- .../navigation/alerts_query_tab_body.tsx | 1 + .../navigation/alerts_query_tab_body.tsx | 1 + .../side_panel/event_details/index.tsx | 6 ++- .../timeline/eql_tab_content/index.tsx | 4 -- .../timeline/notes_tab_content/index.tsx | 4 -- .../timeline/pinned_tab_content/index.tsx | 4 -- .../timeline/query_tab_content/index.tsx | 4 -- .../t_grid/event_rendered_view/index.tsx | 44 ++++++++++++------- .../event_rendered_view/selector/index.tsx | 1 - .../components/t_grid/integrated/index.tsx | 3 +- 15 files changed, 50 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index ddc739b05f4c2..c255702e8de86 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -8,7 +8,6 @@ import React, { useCallback, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { AlertConsumers } from '@kbn/rule-data-utils'; - import { getCaseDetailsUrl, getCaseDetailsUrlWithCommentId, @@ -34,7 +33,6 @@ import { SpyRoute } from '../../../common/utils/route/spy_routes'; import * as timelineMarkdownPlugin from '../../../common/components/markdown_editor/plugins/timeline'; import { CaseDetailsRefreshContext } from '../../../common/components/endpoint/host_isolation/endpoint_host_isolation_cases_context'; import { getEndpointDetailsPath } from '../../../management/common/routing'; -import { EntityType } from '../../../../../timelines/common'; interface Props { caseId: string; @@ -55,7 +53,7 @@ export interface CaseProps extends Props { updateCase: (newCase: Case) => void; } -const ALERT_CONSUMER: AlertConsumers[] = [AlertConsumers.SIEM]; +const SECURITY_SOLUTION_ALERT_CONSUMERS: AlertConsumers[] = [AlertConsumers.SIEM]; const TimelineDetailsPanel = ({ alertConsumers }: { alertConsumers?: AlertConsumers[] }) => { const { browserFields, docValueFields } = useSourcererScope(SourcererScopeName.detections); @@ -65,7 +63,7 @@ const TimelineDetailsPanel = ({ alertConsumers }: { alertConsumers?: AlertConsum alertConsumers={alertConsumers} browserFields={browserFields} docValueFields={docValueFields} - entityType={EntityType.ALERTS} + entityType="alerts" isFlyoutView timelineId={TimelineId.casePage} /> @@ -234,7 +232,7 @@ export const CaseView = React.memo(({ caseId, subCaseId, userCanCrud }: Props) = showAlertDetails, subCaseId, timelineIntegration: { - alertConsumers: ALERT_CONSUMER, + alertConsumers: SECURITY_SOLUTION_ALERT_CONSUMERS, editor_plugins: { parsingPlugin: timelineMarkdownPlugin.parser, processingPluginRenderer: timelineMarkdownPlugin.renderer, diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx index f7cdf60fdd070..fc440197e8349 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx @@ -20,6 +20,7 @@ import { useKibana } from '../../lib/kibana'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; +import type { EntityType } from '../../../../../timelines/common'; export interface OwnProps { end: string; @@ -63,6 +64,7 @@ const defaultAlertsFilters: Filter[] = [ interface Props { timelineId: TimelineIdLiteral; endDate: string; + entityType?: EntityType; startDate: string; pageFilters?: Filter[]; } @@ -70,6 +72,7 @@ interface Props { const AlertsTableComponent: React.FC = ({ timelineId, endDate, + entityType = 'alerts', startDate, pageFilters = [], }) => { @@ -107,7 +110,7 @@ const AlertsTableComponent: React.FC = ({ defaultModel={alertsDefaultModel} defaultCellActions={defaultCellActions} end={endDate} - entityType="alerts" + entityType={entityType} id={timelineId} renderCellValue={DefaultCellRenderer} rowRenderers={defaultRowRenderers} diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx index 36a1e1b941e1b..b0471a72c6ee6 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx @@ -25,6 +25,7 @@ const AlertsViewComponent: React.FC = ({ timelineId, deleteQuery, endDate, + entityType, filterQuery, indexNames, pageFilters, @@ -74,6 +75,7 @@ const AlertsViewComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts index 8da6b79e43d2e..3c439a9eff1ea 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts @@ -6,6 +6,7 @@ */ import { Filter } from '../../../../../../../src/plugins/data/public'; +import type { EntityType } from '../../../../../timelines/common'; import { TimelineIdLiteral } from '../../../../common/types/timeline'; import { HostsComponentsQueryProps } from '../../../hosts/pages/navigation/types'; import { NetworkComponentQueryProps } from '../../../network/pages/navigation/types'; @@ -23,5 +24,6 @@ export interface AlertsComponentsProps stackByOptions?: MatrixHistogramOption[]; defaultFilters?: Filter[]; defaultStackByOption?: MatrixHistogramOption; + entityType?: EntityType; indexNames: string[]; } diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 5496bd2d52c3e..1b21eafc2ba2b 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -11,7 +11,6 @@ import deepEqual from 'fast-deep-equal'; import styled from 'styled-components'; import { isEmpty } from 'lodash/fp'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { inputsModel, inputsSelectors, State } from '../../store'; import { inputsActions } from '../../store/actions'; import { ControlColumnProps, RowRenderer, TimelineId } from '../../../../common/types/timeline'; @@ -24,7 +23,7 @@ import { useGlobalFullScreen } from '../../containers/use_full_screen'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { useSourcererScope } from '../../containers/sourcerer'; -import { EntityType } from '../../../../../timelines/common'; +import type { EntityType } from '../../../../../timelines/common'; import { TGridCellAction } from '../../../../../timelines/common/types'; import { DetailsPanel } from '../../../timelines/components/side_panel'; import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering'; @@ -69,8 +68,6 @@ export interface OwnProps { type Props = OwnProps & PropsFromRedux; -const alertConsumers: AlertConsumers[] = [AlertConsumers.SIEM]; - /** * The stateful events viewer component is the highest level component that is utilized across the security_solution pages layer where * timeline is used BESIDES the flyout. The flyout makes use of the `EventsViewer` component which is a subcomponent here @@ -219,9 +216,8 @@ const StatefulEventsViewerComponent: React.FC = ({ ( = ({ - alertConsumers, + alertConsumers = SECURITY_SOLUTION_ALERT_CONSUMERS, // Default to Security Solution so only other applications have to pass this in browserFields, docValueFields, - entityType, + entityType = 'events', // Default to events so only alerts have to pass entityType in expandedEvent, handleOnEventClosed, isFlyoutView, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx index 28b795378d249..b67b9348f51aa 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx @@ -13,7 +13,6 @@ import { EuiFlyoutFooter, EuiBadge, } from '@elastic/eui'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { isEmpty } from 'lodash/fp'; import React, { useEffect, useCallback } from 'react'; import styled from 'styled-components'; @@ -152,8 +151,6 @@ export type Props = OwnProps & PropsFromRedux; const NO_SORTING: Sort[] = []; -const alertConsumers: AlertConsumers[] = [AlertConsumers.SIEM]; - export const EqlTabContentComponent: React.FC = ({ activeTab, columns, @@ -349,7 +346,6 @@ export const EqlTabContentComponent: React.FC = ({ = ({ timelineId } () => expandedDetail[TimelineTabs.notes]?.panelView ? ( React.ReactNode; rowRenderers: RowRenderer[]; @@ -269,7 +266,6 @@ export const PinnedTabContentComponent: React.FC = ({ theme.eui.paddingSizes.s}; `; -const alertConsumers: AlertConsumers[] = [AlertConsumers.SIEM]; - const isTimerangeSame = (prevProps: Props, nextProps: Props) => prevProps.end === nextProps.end && prevProps.start === nextProps.start && @@ -417,7 +414,6 @@ export const QueryTabContentComponent: React.FC = ({ theme.eui.paddingSizes.m}; + } +`; + // Fix typing issue with EuiBasicTable and styled type BasicTableType = ComponentType>; @@ -113,25 +121,31 @@ const EventRenderedViewComponent = ({ name: ActionTitle, truncateText: false, hideForMobile: false, + // eslint-disable-next-line react/display-name render: (name: unknown, item: unknown) => { const alertId = get(item, '_id'); const rowIndex = events.findIndex((evt) => evt._id === alertId); - return leadingControlColumns.length > 0 - ? leadingControlColumns.map((action) => { - const getActions = action.rowCellRender as ( - props: EuiDataGridCellValueElementProps - ) => React.ReactNode; - return getActions({ - columnId: 'actions', - isDetails: false, - isExpandable: false, - isExpanded: false, - rowIndex, - setCellProps: () => null, - }); - }) - : null; + return ( + + {leadingControlColumns.length > 0 + ? leadingControlColumns.map((action) => { + const getActions = action.rowCellRender as ( + props: EuiDataGridCellValueElementProps + ) => React.ReactNode; + return getActions({ + columnId: 'actions', + isDetails: false, + isExpandable: false, + isExpanded: false, + rowIndex, + setCellProps: () => null, + }); + }) + : null} + + ); }, + width: '120px', }, { field: 'ecs.@timestamp', diff --git a/x-pack/plugins/timelines/public/components/t_grid/event_rendered_view/selector/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/event_rendered_view/selector/index.tsx index 02d20072e7652..3ac2a87382b46 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/event_rendered_view/selector/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/event_rendered_view/selector/index.tsx @@ -142,7 +142,6 @@ const SummaryViewSelectorComponent = ({ viewSelected, onViewChange }: SummaryVie > = ({ loading, { events, loadPage, pageInfo, refetch, totalCount = 0, inspect }, ] = useTimelineEvents({ + // We rely on entityType to determine Events vs Alerts alertConsumers: SECURITY_ALERTS_CONSUMERS, docValueFields, entityType, @@ -302,7 +303,7 @@ const TGridIntegratedComponent: React.FC = ({ {!resolverIsShowing(graphEventId) && additionalFilters} - {tGridEventRenderedViewEnabled && ( + {tGridEventRenderedViewEnabled && entityType === 'alerts' && ( From 8d92668d20ce28327d63def1d1b5223796a15e77 Mon Sep 17 00:00:00 2001 From: mgiota Date: Wed, 18 Aug 2021 13:41:39 +0200 Subject: [PATCH 08/11] [Observability RAC] add filter for value action (#108648) * filter for value * code clean up * fix i18n tests * fix type errors * revert changes to reason field to make reason field clickable again * [RAC Observability] fix reason field * fix type issues * filter my kibana.alert. status on load (will refactor) * refactor filter for alert status on load * remove rest params * fix eslint errors * hard code alert status for now, will be fixed in another PR * move filter_for button in a separate file * fix errors * comply with kibana i18n guideines * simpler implementation for default filtering * fix syntax error * fix type errors * fix eslint errors * fix eslint errors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../pages/alerts/alerts_table_t_grid.tsx | 15 ++-- .../pages/alerts/default_cell_actions.tsx | 69 ++++++------------- .../public/pages/alerts/filter_for_value.tsx | 64 +++++++++++++++++ .../public/pages/alerts/index.tsx | 22 +++++- .../public/pages/alerts/render_cell_value.tsx | 8 +-- 5 files changed, 119 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugins/observability/public/pages/alerts/filter_for_value.tsx diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx index dd493affac0b5..135a63b78dc72 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx @@ -15,14 +15,14 @@ import { ALERT_DURATION as ALERT_DURATION_TYPED, ALERT_SEVERITY_LEVEL as ALERT_SEVERITY_LEVEL_TYPED, ALERT_STATUS as ALERT_STATUS_TYPED, - ALERT_RULE_NAME as ALERT_RULE_NAME_TYPED, + ALERT_REASON as ALERT_REASON_TYPED, ALERT_RULE_CONSUMER, } from '@kbn/rule-data-utils'; import { ALERT_DURATION as ALERT_DURATION_NON_TYPED, ALERT_SEVERITY_LEVEL as ALERT_SEVERITY_LEVEL_NON_TYPED, ALERT_STATUS as ALERT_STATUS_NON_TYPED, - ALERT_RULE_NAME as ALERT_RULE_NAME_NON_TYPED, + ALERT_REASON as ALERT_REASON_NON_TYPED, TIMESTAMP, // @ts-expect-error importing from a place other than root because we want to limit what we import from this package } from '@kbn/rule-data-utils/target_node/technical_field_names'; @@ -41,7 +41,6 @@ import { import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import React, { Suspense, useMemo, useState, useCallback } from 'react'; - import { get } from 'lodash'; import { useGetUserAlertsPermissions } from '../../hooks/use_alert_permission'; import type { TimelinesUIStart, TGridType, SortDirection } from '../../../../timelines/public'; @@ -68,7 +67,7 @@ const AlertConsumers: typeof AlertConsumersTyped = AlertConsumersNonTyped; const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED; const ALERT_SEVERITY_LEVEL: typeof ALERT_SEVERITY_LEVEL_TYPED = ALERT_SEVERITY_LEVEL_NON_TYPED; const ALERT_STATUS: typeof ALERT_STATUS_TYPED = ALERT_STATUS_NON_TYPED; -const ALERT_RULE_NAME: typeof ALERT_RULE_NAME_TYPED = ALERT_RULE_NAME_NON_TYPED; +const ALERT_REASON: typeof ALERT_REASON_TYPED = ALERT_REASON_NON_TYPED; interface AlertsTableTGridProps { indexName: string; @@ -77,6 +76,7 @@ interface AlertsTableTGridProps { kuery: string; status: string; setRefetch: (ref: () => void) => void; + addToQuery: (value: string) => void; } interface ObservabilityActionsProps extends ActionProps { @@ -147,8 +147,8 @@ export const columns: Array< displayAsText: i18n.translate('xpack.observability.alertsTGrid.reasonColumnDescription', { defaultMessage: 'Reason', }), + id: ALERT_REASON, linkField: '*', - id: ALERT_RULE_NAME, }, ]; @@ -299,7 +299,7 @@ function ObservabilityActions({ } export function AlertsTableTGrid(props: AlertsTableTGridProps) { - const { indexName, rangeFrom, rangeTo, kuery, status, setRefetch } = props; + const { indexName, rangeFrom, rangeTo, kuery, status, setRefetch, addToQuery } = props; const { timelines } = useKibana<{ timelines: TimelinesUIStart }>().services; const [flyoutAlert, setFlyoutAlert] = useState(undefined); @@ -343,7 +343,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { type, columns, deletedEventIds: [], - defaultCellActions: getDefaultCellActions({ enableFilterActions: false }), + defaultCellActions: getDefaultCellActions({ addToQuery }), end: rangeTo, filters: [], indexNames: [indexName], @@ -388,6 +388,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { rangeTo, setRefetch, status, + addToQuery, ]); const handleFlyoutClose = () => setFlyoutAlert(undefined); const { observabilityRuleTypeRegistry } = usePluginContext(); diff --git a/x-pack/plugins/observability/public/pages/alerts/default_cell_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/default_cell_actions.tsx index 3056b026fc27a..7e166ac99c05f 100644 --- a/x-pack/plugins/observability/public/pages/alerts/default_cell_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/default_cell_actions.tsx @@ -6,16 +6,18 @@ */ import React from 'react'; - +import { i18n } from '@kbn/i18n'; import { ObservabilityPublicPluginsStart } from '../..'; import { getMappedNonEcsValue } from './render_cell_value'; +import FilterForValueButton from './filter_for_value'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { TimelineNonEcsData } from '../../../../timelines/common/search_strategy'; import { TGridCellAction } from '../../../../timelines/common/types/timeline'; import { TimelinesUIStart } from '../../../../timelines/public'; -/** a noop required by the filter in / out buttons */ -const onFilterAdded = () => {}; +export const FILTER_FOR_VALUE = i18n.translate('xpack.observability.hoverActions.filterForValue', { + defaultMessage: 'Filter for value', +}); /** a hook to eliminate the verbose boilerplate required to use common services */ const useKibanaServices = () => { @@ -31,32 +33,10 @@ const useKibanaServices = () => { return { timelines, filterManager }; }; -/** actions for adding filters to the search bar */ -const filterCellActions: TGridCellAction[] = [ - ({ data }: { data: TimelineNonEcsData[][] }) => ({ rowIndex, columnId, Component }) => { - const { timelines, filterManager } = useKibanaServices(); - - const value = getMappedNonEcsValue({ - data: data[rowIndex], - fieldName: columnId, - }); - - return ( - <> - {timelines.getHoverActions().getFilterForValueButton({ - Component, - field: columnId, - filterManager, - onFilterAdded, - ownFocus: false, - showTooltip: false, - value, - })} - - ); - }, +/** actions common to all cells (e.g. copy to clipboard) */ +const commonCellActions: TGridCellAction[] = [ ({ data }: { data: TimelineNonEcsData[][] }) => ({ rowIndex, columnId, Component }) => { - const { timelines, filterManager } = useKibanaServices(); + const { timelines } = useKibanaServices(); const value = getMappedNonEcsValue({ data: data[rowIndex], @@ -65,11 +45,10 @@ const filterCellActions: TGridCellAction[] = [ return ( <> - {timelines.getHoverActions().getFilterOutValueButton({ + {timelines.getHoverActions().getCopyButton({ Component, field: columnId, - filterManager, - onFilterAdded, + isHoverAction: false, ownFocus: false, showTooltip: false, value, @@ -79,31 +58,27 @@ const filterCellActions: TGridCellAction[] = [ }, ]; -/** actions common to all cells (e.g. copy to clipboard) */ -const commonCellActions: TGridCellAction[] = [ +/** actions for adding filters to the search bar */ +const buildFilterCellActions = (addToQuery: (value: string) => void): TGridCellAction[] => [ ({ data }: { data: TimelineNonEcsData[][] }) => ({ rowIndex, columnId, Component }) => { - const { timelines } = useKibanaServices(); - const value = getMappedNonEcsValue({ data: data[rowIndex], fieldName: columnId, }); return ( - <> - {timelines.getHoverActions().getCopyButton({ - Component, - field: columnId, - isHoverAction: false, - ownFocus: false, - showTooltip: false, - value, - })} - + ); }, ]; /** returns the default actions shown in `EuiDataGrid` cells */ -export const getDefaultCellActions = ({ enableFilterActions }: { enableFilterActions: boolean }) => - enableFilterActions ? [...filterCellActions, ...commonCellActions] : [...commonCellActions]; +export const getDefaultCellActions = ({ addToQuery }: { addToQuery: (value: string) => void }) => [ + ...buildFilterCellActions(addToQuery), + ...commonCellActions, +]; diff --git a/x-pack/plugins/observability/public/pages/alerts/filter_for_value.tsx b/x-pack/plugins/observability/public/pages/alerts/filter_for_value.tsx new file mode 100644 index 0000000000000..77cac9d482a37 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/filter_for_value.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; + +export const filterForValueButtonLabel = i18n.translate( + 'xpack.observability.hoverActions.filterForValueButtonLabel', + { + defaultMessage: 'Filter for value', + } +); + +import { EuiButtonIcon, EuiButtonEmpty } from '@elastic/eui'; + +interface FilterForValueProps { + Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; + field: string; + value: string[] | string | null | undefined; + addToQuery: (value: string) => void; +} + +const FilterForValueButton: React.FC = React.memo( + ({ Component, field, value, addToQuery }) => { + const text = useMemo(() => `${field}${value != null ? `: "${value}"` : ''}`, [field, value]); + const onClick = useCallback(() => { + addToQuery(text); + }, [text, addToQuery]); + const button = useMemo( + () => + Component ? ( + + {filterForValueButtonLabel} + + ) : ( + + ), + [Component, onClick] + ); + return button; + } +); + +FilterForValueButton.displayName = 'FilterForValueButton'; + +// eslint-disable-next-line import/no-default-export +export { FilterForValueButton as default }; diff --git a/x-pack/plugins/observability/public/pages/alerts/index.tsx b/x-pack/plugins/observability/public/pages/alerts/index.tsx index baed76d49aac8..b3ff3f94dc4db 100644 --- a/x-pack/plugins/observability/public/pages/alerts/index.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/index.tsx @@ -40,7 +40,12 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { const history = useHistory(); const refetch = useRef<() => void>(); const { - query: { rangeFrom = 'now-15m', rangeTo = 'now', kuery = '', status = 'open' }, + query: { + rangeFrom = 'now-15m', + rangeTo = 'now', + kuery = 'kibana.alert.status: "open"', // TODO change hardcoded values as part of another PR + status = 'open', + }, } = routeParams; useBreadcrumbs([ @@ -98,6 +103,20 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { [history, rangeFrom, rangeTo, kuery] ); + const addToQuery = useCallback( + (value: string) => { + let output = value; + if (kuery !== '') { + output = `${kuery} and ${value}`; + } + onQueryChange({ + dateRange: { from: rangeFrom, to: rangeTo }, + query: output, + }); + }, + [kuery, onQueryChange, rangeFrom, rangeTo] + ); + const setRefetch = useCallback((ref) => { refetch.current = ref; }, []); @@ -170,6 +189,7 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { kuery={kuery} status={status} setRefetch={setRefetch} + addToQuery={addToQuery} /> diff --git a/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx b/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx index 128f22d707a69..34b595ddc34f3 100644 --- a/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx @@ -16,13 +16,13 @@ import type { ALERT_DURATION as ALERT_DURATION_TYPED, ALERT_SEVERITY_LEVEL as ALERT_SEVERITY_LEVEL_TYPED, ALERT_STATUS as ALERT_STATUS_TYPED, - ALERT_RULE_NAME as ALERT_RULE_NAME_TYPED, + ALERT_REASON as ALERT_REASON_TYPED, } from '@kbn/rule-data-utils'; import { ALERT_DURATION as ALERT_DURATION_NON_TYPED, ALERT_SEVERITY_LEVEL as ALERT_SEVERITY_LEVEL_NON_TYPED, ALERT_STATUS as ALERT_STATUS_NON_TYPED, - ALERT_RULE_NAME as ALERT_RULE_NAME_NON_TYPED, + ALERT_REASON as ALERT_REASON_NON_TYPED, TIMESTAMP, // @ts-expect-error importing from a place other than root because we want to limit what we import from this package } from '@kbn/rule-data-utils/target_node/technical_field_names'; @@ -39,7 +39,7 @@ import { useTheme } from '../../hooks/use_theme'; const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED; const ALERT_SEVERITY_LEVEL: typeof ALERT_SEVERITY_LEVEL_TYPED = ALERT_SEVERITY_LEVEL_NON_TYPED; const ALERT_STATUS: typeof ALERT_STATUS_TYPED = ALERT_STATUS_NON_TYPED; -const ALERT_RULE_NAME: typeof ALERT_RULE_NAME_TYPED = ALERT_RULE_NAME_NON_TYPED; +const ALERT_REASON: typeof ALERT_REASON_TYPED = ALERT_REASON_NON_TYPED; export const getMappedNonEcsValue = ({ data, @@ -120,7 +120,7 @@ export const getRenderCellValue = ({ return asDuration(Number(value)); case ALERT_SEVERITY_LEVEL: return ; - case ALERT_RULE_NAME: + case ALERT_REASON: const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {}); const alert = parseAlert(observabilityRuleTypeRegistry)(dataFieldEs); From a2be619c9899fd7530a18b1977d6df9603347e31 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 18 Aug 2021 07:21:04 -0500 Subject: [PATCH 09/11] Fix a bug where role mappings not populated (#109008) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a bug where adding or deleting a role mapping resulted in the table not being updated. The issue stems from the fact that a copy of the mappings called “items” is created with loca state in the component for filtering before passing to EuiBasicTable. The issue is that this works fine on initial load, but the copy of items is never updated on subsequent renders. The solution is to update the items each time roleMappings is updated. --- .../shared/role_mapping/role_mappings_table.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx index d6299bc1b3896..6e213edf457b1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { EuiIconTip, EuiInMemoryTable, EuiBasicTableColumn, EuiLink } from '@elastic/eui'; import type { EuiSearchBarOnChangeArgs } from '@elastic/eui'; @@ -71,7 +71,11 @@ export const RoleMappingsTable: React.FC = ({ return _rm; }) as SharedRoleMapping[]; - const [items, setItems] = useState(standardizedRoleMappings); + const [items, setItems] = useState([] as SharedRoleMapping[]); + + useEffect(() => { + setItems(standardizedRoleMappings); + }, [roleMappings]); const attributeNameCol: EuiBasicTableColumn = { field: 'attribute', From 74b2a3c3830d69f88c5931c685eb29c4a75c7ef1 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 18 Aug 2021 15:27:02 +0300 Subject: [PATCH 10/11] [Lens] Add retry to drag and drop to fix flakiness (#108657) * [Lens] Add retry to drag and drop to fix flakiness * Unskip test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/functional/apps/lens/drag_and_drop.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/drag_and_drop.ts index 8dd500755242b..c8a0e171d5a79 100644 --- a/x-pack/test/functional/apps/lens/drag_and_drop.ts +++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts @@ -10,16 +10,18 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); + const retry = getService('retry'); describe('lens drag and drop tests', () => { - // FLAKY: https://github.com/elastic/kibana/issues/108352 - describe.skip('basic drag and drop', () => { + describe('basic drag and drop', () => { it('should construct the basic split xy chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.goToTimeRange(); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + await retry.try(async () => { + await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + }); expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( '@timestamp' From 8f7e10aaba344e28be2622b85441be4cff297717 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Aug 2021 14:36:21 +0200 Subject: [PATCH 11/11] [Reporting][Revert] Deprecate v1 report types (#109058) --- .../reporting/server/export_types/png/create_job/index.ts | 3 +-- .../server/export_types/printable_pdf/create_job/index.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts index a2f58e5835f22..aae2e85e823db 100644 --- a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts @@ -22,11 +22,10 @@ export const createJobFnFactory: CreateJobFnFactory< validateUrls([jobParams.relativeUrl]); return { - isDeprecated: true, + ...jobParams, headers: serializedEncryptedHeaders, spaceId: reporting.getSpaceId(req, logger), forceNow: new Date().toISOString(), - ...jobParams, }; }; }; diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts index 011d0f237d3e6..8411dbcb94d11 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts @@ -34,7 +34,6 @@ export const createJobFnFactory: CreateJobFnFactory< // return the payload return { ...jobParams, - isDeprecated: true, headers: serializedEncryptedHeaders, spaceId: reporting.getSpaceId(req, logger), forceNow: new Date().toISOString(),