From 9c09111729fb198f05b050b97de7646fdb79fbb7 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 12 Sep 2023 13:06:52 +0200 Subject: [PATCH 01/61] Add dashboards svg --- packages/kbn-shared-svg/index.ts | 4 +++- packages/kbn-shared-svg/src/assets/dashboards_dark.svg | 1 + packages/kbn-shared-svg/src/assets/dashboards_light.svg | 1 + x-pack/plugins/apm/tsconfig.json | 3 ++- 4 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 packages/kbn-shared-svg/src/assets/dashboards_dark.svg create mode 100644 packages/kbn-shared-svg/src/assets/dashboards_light.svg diff --git a/packages/kbn-shared-svg/index.ts b/packages/kbn-shared-svg/index.ts index 61ffc0f763cba..215431706ab94 100644 --- a/packages/kbn-shared-svg/index.ts +++ b/packages/kbn-shared-svg/index.ts @@ -8,5 +8,7 @@ import noResultsIllustrationDark from './src/assets/no_results_dark.svg'; import noResultsIllustrationLight from './src/assets/no_results_light.svg'; +import dashboardsLight from './src/assets/dashboards_light.svg'; +import dashboardsDark from './src/assets/dashboards_dark.svg'; -export { noResultsIllustrationDark, noResultsIllustrationLight }; +export { noResultsIllustrationDark, noResultsIllustrationLight, dashboardsLight, dashboardsDark }; diff --git a/packages/kbn-shared-svg/src/assets/dashboards_dark.svg b/packages/kbn-shared-svg/src/assets/dashboards_dark.svg new file mode 100644 index 0000000000000..6499131ce6b5a --- /dev/null +++ b/packages/kbn-shared-svg/src/assets/dashboards_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/kbn-shared-svg/src/assets/dashboards_light.svg b/packages/kbn-shared-svg/src/assets/dashboards_light.svg new file mode 100644 index 0000000000000..4ca82bfe3ff98 --- /dev/null +++ b/packages/kbn-shared-svg/src/assets/dashboards_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index edfcdda7a2006..582292a0b4948 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -100,7 +100,8 @@ "@kbn/profiling-utils", "@kbn/core-analytics-server", "@kbn/analytics-client", - "@kbn/monaco" + "@kbn/monaco", + "@kbn/shared-svg" ], "exclude": ["target/**/*"] } From 32cfd918eb8005b988c2b96b02ce986cf7ef861d Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 12 Sep 2023 15:24:25 +0200 Subject: [PATCH 02/61] Introduce dashboards tab --- .../components/routing/service_detail/index.tsx | 10 ++++++++++ .../templates/apm_service_template/index.tsx | 14 +++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx index 5817b96e2b360..17f49dc9b4538 100644 --- a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx @@ -39,6 +39,7 @@ import { ApmServiceWrapper } from './apm_service_wrapper'; import { RedirectToDefaultServiceRouteView } from './redirect_to_default_service_route_view'; import { ProfilingOverview } from '../../app/profiling_overview'; import { SearchBar } from '../../shared/search_bar/search_bar'; +import { ServiceDashboards } from '../../app/service_dashboards'; function page({ title, @@ -376,6 +377,15 @@ export const serviceDetailRoute = { }, }), }, + '/services/{serviceName}/dashboards': { + ...page({ + tab: 'dashboards', + title: i18n.translate('xpack.apm.views.dashboard.title', { + defaultMessage: 'Dashboards', + }), + element: , + }), + }, '/services/{serviceName}/': { element: , }, diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx index 73c53dd5fda91..ddcf85986ab58 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx @@ -62,7 +62,8 @@ type Tab = NonNullable[0] & { | 'service-map' | 'logs' | 'alerts' - | 'profiling'; + | 'profiling' + | 'dashboards'; hidden?: boolean; }; @@ -412,6 +413,17 @@ function useTabs({ selectedTab }: { selectedTab: Tab['key'] }) { ), }, + { + key: 'dashboards', + href: router.link('/services/{serviceName}/dashboards', { + path: { serviceName }, + query, + }), + label: i18n.translate('xpack.apm.home.dashboardsTabLabel', { + defaultMessage: 'Dashboards', + }), + append: , + }, ]; return tabs From 435f1334db221f2a012cfc4cae38029c7b10b3ce Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 12 Sep 2023 15:24:44 +0200 Subject: [PATCH 03/61] Add empty component --- .../service_dashboards/empty_dashboards.tsx | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx new file mode 100644 index 0000000000000..2e7c1502e65ce --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiEmptyPrompt, EuiImage } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg'; +import { useTheme } from '../../../hooks/use_theme'; +import { AddButton } from './actions/add_dashboard'; + +export function EmptyDashboards() { + const theme = useTheme(); + return ( + + } + title={ +

+ {i18n.translate('xpack.apm.serviceDashboards.emptyTitle', { + defaultMessage: + 'The best way to understand your data is to visualize it.', + })} +

+ } + layout="horizontal" + color="plain" + body={ + <> +
    +
  • + {i18n.translate('xpack.apm.serviceDashboards.emptyBody.first', { + defaultMessage: 'bring clarity to your data', + })} +
  • +
  • + {i18n.translate('xpack.apm.serviceDashboards.emptyBody.second', { + defaultMessage: 'tell a story about your data', + })} +
  • +
  • + {i18n.translate('xpack.apm.serviceDashboards.emptyBody', { + defaultMessage: + 'focus on only the data that’s important to you', + })} +
  • +
+

+ {i18n.translate('xpack.apm.serviceDashboards.emptyTitle', { + defaultMessage: 'To get started, add your dashaboard', + })} +

+ + } + actions={} + /> + ); +} From 448478f14b5a0018650f68ab6fad8c589b417a2b Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 12 Sep 2023 21:22:25 +0200 Subject: [PATCH 04/61] List available dashboards to select --- .../actions/add_dashboard.tsx | 31 +++++++++ .../app/service_dashboards/actions/index.ts | 10 +++ .../actions/select_dashboard.tsx | 67 +++++++++++++++++++ .../dashboards_dropdown_list.tsx | 44 ++++++++++++ .../service_dashboards/empty_dashboards.tsx | 9 ++- .../app/service_dashboards/index.tsx | 25 +++++++ .../public/hooks/use_dashboards_fetcher.ts | 57 ++++++++++++++++ x-pack/plugins/apm/public/plugin.ts | 3 +- 8 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/actions/index.ts create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx create mode 100644 x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx new file mode 100644 index 0000000000000..910528a77a3fa --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useState } from 'react'; +import { SelectDashboard } from './select_dashboard'; + +export function AddDashboard() { + const [isModalVisible, setIsModalVisible] = useState(false); + + return ( + <> + setIsModalVisible(true)} + > + {i18n.translate('xpack.apm.serviceDashboards.addButtonLabel', { + defaultMessage: 'Add dashboard', + })} + + + {isModalVisible && ( + setIsModalVisible(false)} /> + )} + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/index.ts b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/index.ts new file mode 100644 index 0000000000000..9dba98395160e --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AddDashboard } from './add_dashboard'; + +export { AddDashboard }; diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx new file mode 100644 index 0000000000000..1f168297787d6 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { + EuiButton, + EuiModal, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSwitch, + EuiModalBody, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { DashboardsDropdownList } from '../dashboards_dropdown_list'; + +interface Props { + onClose: () => void; +} + +export function SelectDashboard({ onClose }: Props) { + return ( + + + + {' '} + {i18n.translate( + 'xpack.apm.serviceDashboards.selectDashboard.modalTitle', + { + defaultMessage: 'Select dashboard', + } + )} + + + + + + console.log('r')} + checked={false} + compressed + /> + + + + + {i18n.translate( + 'xpack.apm.serviceDashboards.selectDashboard.cancel', + { + defaultMessage: 'Cancel', + } + )} + + + {i18n.translate('xpack.apm.serviceDashboards.selectDashboard.add', { + defaultMessage: 'Add dashboard', + })} + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx new file mode 100644 index 0000000000000..33a6a7f7d7aad --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; + +export function DashboardsDropdownList() { + const { data, status } = useDashboardFetcher(); + const [selectedOptions, setSelected] = useState([]); + + const onChange = ( + selectedOptions: Array> + ) => { + setSelected(selectedOptions); + }; + + return ( + ({ + label: dashboardItem.attributes.title, + value: dashboardItem.id, + }))} + selectedOptions={selectedOptions} + onChange={onChange} + isClearable={true} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx index 2e7c1502e65ce..1283699c75b2e 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx @@ -9,9 +9,12 @@ import { EuiEmptyPrompt, EuiImage } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg'; import { useTheme } from '../../../hooks/use_theme'; -import { AddButton } from './actions/add_dashboard'; -export function EmptyDashboards() { +interface Props { + actions: React.ReactNode; +} + +export function EmptyDashboards({ actions }: Props) { const theme = useTheme(); return ( } - actions={} + actions={actions} /> ); } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx new file mode 100644 index 0000000000000..57c7ba740a637 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import { EmptyDashboards } from './empty_dashboards'; +import { AddDashboard } from './actions'; + +export function ServiceDashboards() { + return ( + + + Title Placeholder + + + + + } /> + + ); +} diff --git a/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts new file mode 100644 index 0000000000000..db31c771a0bd1 --- /dev/null +++ b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useEffect } from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ApmPluginStartDeps } from '../plugin'; +import { FETCH_STATUS } from './use_fetcher'; +import { DashboardItem } from '../../../../../src/plugins/dashboard/common/content_management'; + +export interface FetcherResult { + data?: DashboardItem[]; + status: FETCH_STATUS; +} + +export function useDashboardFetcher(query?: string): FetcherResult { + const { + services: { dashboard }, + } = useKibana(); + + const [result, setResult] = useState({ + data: [], + status: FETCH_STATUS.NOT_INITIATED, + }); + + useEffect(() => { + const getDashboards = async () => { + console.log('getDashboards'); + setResult({ + data: [], + status: FETCH_STATUS.LOADING, + }); + try { + const findDashboardsService = await dashboard?.findDashboardsService(); + const data = await findDashboardsService.search({ + search: query ?? '', + size: 1000, + }); + + setResult({ + data: data?.hits ?? [], + status: FETCH_STATUS.SUCCESS, + }); + } catch { + setResult({ + data: [], + status: FETCH_STATUS.FAILURE, + }); + } + }; + getDashboards(); + }, []); + return result; +} diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 800fb6bf123cd..f9206b8aaa782 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -69,6 +69,7 @@ import type { import { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import { DashboardStart } from '@kbn/dashboard-plugin/public'; import { from } from 'rxjs'; import { map } from 'rxjs/operators'; import type { ConfigSchema } from '.'; @@ -84,7 +85,6 @@ import { featureCatalogueEntry } from './feature_catalogue_entry'; import { APMServiceDetailLocator } from './locator/service_detail_locator'; import { ITelemetryClient, TelemetryService } from './services/telemetry'; export type ApmPluginSetup = ReturnType; - export type ApmPluginStart = void; export interface ApmPluginSetupDeps { @@ -136,6 +136,7 @@ export interface ApmPluginStartDeps { uiActions: UiActionsStart; profiling?: ProfilingPluginStart; observabilityAIAssistant: ObservabilityAIAssistantPluginStart; + dashboard: DashboardStart; } const servicesTitle = i18n.translate('xpack.apm.navigation.servicesTitle', { From 87d3c058b6c2c717d86420401114cd7bdf820a7c Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 13 Sep 2023 09:43:04 +0200 Subject: [PATCH 05/61] Add dashboard as a saved object --- .../plugins/apm/common/service_dashboards.ts | 21 ++++ .../actions/select_dashboard.tsx | 99 ++++++++++++++++++- .../dashboards_dropdown_list.tsx | 44 --------- x-pack/plugins/apm/server/plugin.ts | 2 + .../get_global_apm_server_route_repository.ts | 2 + .../server/routes/service_dashboards/route.ts | 48 +++++++++ .../save_service_dashboard.ts | 39 ++++++++ .../saved_objects/apm_service_dashboards.ts | 48 +++++++++ .../plugins/apm/server/saved_objects/index.ts | 1 + 9 files changed, 256 insertions(+), 48 deletions(-) create mode 100644 x-pack/plugins/apm/common/service_dashboards.ts delete mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx create mode 100644 x-pack/plugins/apm/server/routes/service_dashboards/route.ts create mode 100644 x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts create mode 100644 x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts new file mode 100644 index 0000000000000..228dd627e01f6 --- /dev/null +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE = 'apm-service-dashboard'; + +export interface ServiceDashboard { + id: string; + title: string; + serviceName?: string; + environment?: string; + kuery: string; +} + +export interface SavedServiceDashboard extends ServiceDashboard { + id: string; + updatedAt: number; +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx index 1f168297787d6..5f5d174e14b70 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx @@ -5,7 +5,9 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + import { EuiButton, EuiModal, @@ -14,15 +16,86 @@ import { EuiModalHeaderTitle, EuiSwitch, EuiModalBody, + EuiComboBox, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { DashboardsDropdownList } from '../dashboards_dropdown_list'; +import { callApmApi } from '../../../../services/rest/create_call_apm_api'; +import { ServiceDashboard } from '../../../../../common/service_dashboards'; +import { useDashboardFetcher } from '../../../../hooks/use_dashboards_fetcher'; +import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; interface Props { onClose: () => void; } export function SelectDashboard({ onClose }: Props) { + const { + core: { notifications }, + } = useApmPluginContext(); + + const { data, status } = useDashboardFetcher(); + const [selectedDashboard, setSelectedDashboard] = useState([]); + + console.log('selectedDashboard', selectedDashboard); + + // TODO need to refetch and not reload + const reloadServiceDashboards = useCallback(() => { + window.location.reload(); + }, []); + + const onSave = useCallback( + async function (newDashboard: ServiceDashboard) { + const [newDashboard] = selectedDashboard; + // setIsLoading(true); + try { + await callApmApi('POST /internal/apm/service-dashboard', { + params: { + body: { + title: newDashboard.label, + id: newDashboard.value, + kuery: '', + environment: '', + serviceName: '', + }, + }, + signal: null, + }); + notifications.toasts.addSuccess({ + title: i18n.translate( + 'xpack.apm.serviceDashboards.addSuccess.toast.title', + { + defaultMessage: 'Added "{dashboardName}" dashboard', + values: { dashboardName: newDashboard.label }, + } + ), + text: i18n.translate( + 'xpack.apm.serviceDashboards.addSuccess.toast.text', + { + defaultMessage: + 'Your dashboard is now visible in the service overview page.', + } + ), + }); + reloadServiceDashboards(); + } catch (error) { + console.error(error); + notifications.toasts.addDanger({ + title: i18n.translate( + 'xpack.apm.serviceDashboards.addFailure.toast.title', + { + defaultMessage: 'Error while adding "{dashboardName}" dashboard', + values: { dashboardName: newDashboard.label }, + } + ), + text: error.body.message, + }); + } + onClose(); + }, + [selectedDashboard, notifications.toasts] + ); + return ( @@ -38,7 +111,25 @@ export function SelectDashboard({ onClose }: Props) { - + ({ + label: dashboardItem.attributes.title, + value: dashboardItem.id, + }))} + selectedOptions={selectedDashboard} + onChange={(newSelection) => setSelectedDashboard(newSelection)} + isClearable={true} + /> + console.log('r')} @@ -56,7 +147,7 @@ export function SelectDashboard({ onClose }: Props) { } )} - + {i18n.translate('xpack.apm.serviceDashboards.selectDashboard.add', { defaultMessage: 'Add dashboard', })} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx deleted file mode 100644 index 33a6a7f7d7aad..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx +++ /dev/null @@ -1,44 +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 { i18n } from '@kbn/i18n'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; -import { FETCH_STATUS } from '../../../hooks/use_fetcher'; - -export function DashboardsDropdownList() { - const { data, status } = useDashboardFetcher(); - const [selectedOptions, setSelected] = useState([]); - - const onChange = ( - selectedOptions: Array> - ) => { - setSelected(selectedOptions); - }; - - return ( - ({ - label: dashboardItem.attributes.title, - value: dashboardItem.id, - }))} - selectedOptions={selectedOptions} - onChange={onChange} - isClearable={true} - /> - ); -} diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 79e5f353f3ce3..685624401aba9 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -32,6 +32,7 @@ import { apmTelemetry, apmServerSettings, apmServiceGroups, + apmServiceDashboards, } from './saved_objects'; import { APMPluginSetup, @@ -75,6 +76,7 @@ export class APMPlugin core.savedObjects.registerType(apmTelemetry); core.savedObjects.registerType(apmServerSettings); core.savedObjects.registerType(apmServiceGroups); + core.savedObjects.registerType(apmServiceDashboards); const currentConfig = this.initContext.config.get(); this.currentConfig = currentConfig; diff --git a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts index 4186523029c99..504659fdf2750 100644 --- a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts +++ b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts @@ -46,6 +46,7 @@ import { traceRouteRepository } from '../traces/route'; import { transactionRouteRepository } from '../transactions/route'; import { assistantRouteRepository } from '../assistant_functions/route'; import { profilingRouteRepository } from '../profiling/route'; +import { serviceDashboardsRouteRepository } from '../service_dashboards/route'; function getTypedGlobalApmServerRouteRepository() { const repository = { @@ -85,6 +86,7 @@ function getTypedGlobalApmServerRouteRepository() { ...diagnosticsRepository, ...assistantRouteRepository, ...profilingRouteRepository, + ...serviceDashboardsRouteRepository, }; return repository; diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts new file mode 100644 index 0000000000000..0a052623cf87e --- /dev/null +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; +import { saveServiceDashbord } from './save_service_dashboard'; +import { SavedServiceDashboard } from '../../../common/service_dashboards'; + +const serviceDashboardSaveRoute = createApmServerRoute({ + endpoint: 'POST /internal/apm/service-dashboard', + params: t.type({ + query: t.union([ + t.partial({ + serviceDashboardId: t.string, + }), + t.undefined, + ]), + body: t.type({ + id: t.string, + title: t.string, + kuery: t.string, + serviceName: t.union([t.string, t.undefined]), + environment: t.union([t.string, t.undefined]), + }), + }), + options: { tags: ['access:apm', 'access:apm_write'] }, + handler: async (resources): Promise => { + const { context, params } = resources; + const { serviceDashboardId } = params.query; + const { + savedObjects: { client: savedObjectsClient }, + } = await context.core; + + return saveServiceDashbord({ + savedObjectsClient, + serviceDashboardId, + serviceDashboard: params.body, + }); + }, +}); + +export const serviceDashboardsRouteRepository = { + ...serviceDashboardSaveRoute, +}; diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts new file mode 100644 index 0000000000000..7e287953d3854 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { + APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + SavedServiceDashboard, + ServiceDashboard, +} from '../../../common/service_dashboards'; + +interface Options { + savedObjectsClient: SavedObjectsClientContract; + serviceDashboardId?: string; + serviceDashboard: ServiceDashboard; +} +export async function saveServiceDashbord({ + savedObjectsClient, + serviceDashboardId, + serviceDashboard, +}: Options): Promise { + const { attributes, updated_at: updatedAt } = await (serviceDashboardId + ? savedObjectsClient.update( + APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + serviceDashboardId, + serviceDashboard + ) + : savedObjectsClient.create( + APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + serviceDashboard + )); + return { + ...(attributes as ServiceDashboard), + updatedAt: updatedAt ? Date.parse(updatedAt) : 0, + }; +} diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts new file mode 100644 index 0000000000000..9dacbb3d18a4c --- /dev/null +++ b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsType } from '@kbn/core/server'; +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; +import { APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE } from '../../common/service_dashboards'; + +export const apmServiceDashboards: SavedObjectsType = { + name: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + hidden: false, + namespaceType: 'multiple', + mappings: { + properties: { + id: { type: 'keyword' }, + title: { type: 'text' }, + kuery: { type: 'text' }, + serviceName: { type: 'keyword' }, + environment: { type: 'keyword' }, + }, + }, + management: { + importableAndExportable: false, + icon: 'apmApp', + getTitle: () => + i18n.translate('xpack.apm.apmServiceDashboards.title', { + defaultMessage: 'APM Service Custom Dashboards', + }), + }, + modelVersions: { + '1': { + changes: [], + schemas: { + create: schema.object({ + id: schema.string(), + title: schema.string(), + kuery: schema.string(), + serviceName: schema.maybe(schema.string()), + environment: schema.maybe(schema.string()), + }), + }, + }, + }, +}; diff --git a/x-pack/plugins/apm/server/saved_objects/index.ts b/x-pack/plugins/apm/server/saved_objects/index.ts index b39e032ad14bd..a598522b6af2c 100644 --- a/x-pack/plugins/apm/server/saved_objects/index.ts +++ b/x-pack/plugins/apm/server/saved_objects/index.ts @@ -8,3 +8,4 @@ export { apmTelemetry } from './apm_telemetry'; export { apmServerSettings } from './apm_server_settings'; export { apmServiceGroups } from './apm_service_groups'; +export { apmServiceDashboards } from './apm_service_dashboards'; From 75973878bede2acc75489a4c9a4ca867fa89dce0 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 13 Sep 2023 12:02:20 +0200 Subject: [PATCH 06/61] wip --- .../plugins/apm/common/service_dashboards.ts | 6 +-- .../actions/select_dashboard.tsx | 16 ++++--- .../dashboards_dropdown_list.tsx | 44 ++++++++++++++++++ .../app/service_dashboards/index.tsx | 40 ++++++++++++++++- .../collect_data_telemetry/tasks.ts | 19 +------- .../get_service_dashboards.ts | 45 +++++++++++++++++++ .../server/routes/service_dashboards/route.ts | 39 ++++++++++++++-- .../save_service_dashboard.ts | 7 ++- .../saved_objects/apm_service_dashboards.ts | 8 ++-- 9 files changed, 187 insertions(+), 37 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx create mode 100644 x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index 228dd627e01f6..2e691fee3baeb 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -8,9 +8,9 @@ export const APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE = 'apm-service-dashboard'; export interface ServiceDashboard { - id: string; - title: string; - serviceName?: string; + dashboardSavedObjectId: string; + dashboardTitle: string; + serviceName: string; environment?: string; kuery: string; } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx index 5f5d174e14b70..dbaf7e7859207 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx @@ -6,8 +6,6 @@ */ import React, { useCallback, useState } from 'react'; -import { useHistory } from 'react-router-dom'; - import { EuiButton, EuiModal, @@ -20,10 +18,10 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { callApmApi } from '../../../../services/rest/create_call_apm_api'; -import { ServiceDashboard } from '../../../../../common/service_dashboards'; import { useDashboardFetcher } from '../../../../hooks/use_dashboards_fetcher'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { useApmParams } from '../../../../hooks/use_apm_params'; interface Props { onClose: () => void; @@ -37,6 +35,10 @@ export function SelectDashboard({ onClose }: Props) { const { data, status } = useDashboardFetcher(); const [selectedDashboard, setSelectedDashboard] = useState([]); + const { + path: { serviceName }, + } = useApmParams('/services/{serviceName}/dashboards'); + console.log('selectedDashboard', selectedDashboard); // TODO need to refetch and not reload @@ -45,18 +47,18 @@ export function SelectDashboard({ onClose }: Props) { }, []); const onSave = useCallback( - async function (newDashboard: ServiceDashboard) { + async function () { const [newDashboard] = selectedDashboard; // setIsLoading(true); try { await callApmApi('POST /internal/apm/service-dashboard', { params: { body: { - title: newDashboard.label, - id: newDashboard.value, + dashboardTitle: newDashboard.label, + dashboardSavedObjectId: newDashboard.value, kuery: '', environment: '', - serviceName: '', + serviceName, }, }, signal: null, diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx new file mode 100644 index 0000000000000..3613a9efcb721 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; + +export function DashboardsDropdownList(selectedDashboard, onChangeDashboard) { + const { data, status } = useDashboardFetcher(); + const [selectedOptions, setSelected] = useState([]); + + const onChange = ( + selectedOptions: Array> + ) => { + onChangeDashboard(selectedOptions); + }; + + return ( + ({ + label: dashboardItem.attributes.title, + value: dashboardItem.id, + }))} + selectedOptions={selectedDashboard} + onChange={onChange} + isClearable={true} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 57c7ba740a637..19d68eabca7ed 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -9,8 +9,37 @@ import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EmptyDashboards } from './empty_dashboards'; import { AddDashboard } from './actions'; +import { useFetcher } from '../../../hooks/use_fetcher'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { + AwaitingDashboardAPI, + DashboardRenderer, +} from '@kbn/dashboard-plugin/public'; export function ServiceDashboards() { + const { + path: { serviceName }, + query: { environment, kuery, rangeFrom, rangeTo }, + } = useApmParams('/services/{serviceName}/dashboards'); + + const { data, status } = useFetcher( + (callApmApi) => { + if (serviceName) { + return callApmApi( + `GET /internal/apm/services/{serviceName}/dashboards`, + { + params: { + path: { serviceName }, + }, + } + ); + } + }, + [serviceName] + ); + + console.log('data====', data); + return ( @@ -19,7 +48,16 @@ export function ServiceDashboards() { - } /> + // TODO add loading + {data && data?.serviceSpecificDashboards ? ( + + ) : ( + } /> + )} ); } diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index beb84481e84b7..31ca9a9727b69 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -558,24 +558,7 @@ export const tasks: TelemetryTask[] = [ }, { name: 'agent_configuration', - executor: async ({ indices, telemetryClient }) => { - const agentConfigurationCount = await telemetryClient.search({ - index: APM_AGENT_CONFIGURATION_INDEX, - body: { - size: 0, - timeout, - track_total_hits: true, - }, - }); - - return { - counts: { - agent_configuration: { - all: agentConfigurationCount.hits.total.value, - }, - }, - }; - }, + executor: async ({ indices, telemetryClient }) => {}, }, { name: 'services', diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts new file mode 100644 index 0000000000000..1429559de7e9c --- /dev/null +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { + APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + SavedServiceDashboard, + ServiceDashboard, +} from '../../../common/service_dashboards'; + +interface Props { + savedObjectsClient: SavedObjectsClientContract; + serviceName: string; +} + +export async function getServiceDashboards({ + savedObjectsClient, + serviceName, +}: Props): Promise { + // savedObjectsClient.bulkDelete(APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE); + const result = await savedObjectsClient.find({ + type: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + page: 1, + perPage: 10, + }); + + console.log('result====', result); + // const opt = result.saved_objects.map( + // ({ id, type, attributes, updated_at: upatedAt }) => ({ type, id }) + // ); + + // savedObjectsClient.bulkDelete(opt); + + return result.saved_objects.map( + ({ id, attributes, updated_at: upatedAt }) => ({ + id, + updatedAt: upatedAt ? Date.parse(upatedAt) : 0, + ...attributes, + }) + ); +} diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index 0a052623cf87e..d6339cb05da72 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { saveServiceDashbord } from './save_service_dashboard'; import { SavedServiceDashboard } from '../../../common/service_dashboards'; +import { getServiceDashboards } from './get_service_dashboards'; const serviceDashboardSaveRoute = createApmServerRoute({ endpoint: 'POST /internal/apm/service-dashboard', @@ -20,10 +21,10 @@ const serviceDashboardSaveRoute = createApmServerRoute({ t.undefined, ]), body: t.type({ - id: t.string, - title: t.string, + dashboardSavedObjectId: t.string, + dashboardTitle: t.string, kuery: t.string, - serviceName: t.union([t.string, t.undefined]), + serviceName: t.string, environment: t.union([t.string, t.undefined]), }), }), @@ -43,6 +44,38 @@ const serviceDashboardSaveRoute = createApmServerRoute({ }, }); +const serviceDashboardsRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/services/{serviceName}/dashboards', + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + }), + options: { + tags: ['access:apm'], + }, + handler: async ( + resources + ): Promise<{ serviceSpecificDashboards: SavedServiceDashboard[] }> => { + const { context, params } = resources; + const { serviceName } = params.path; + + const { + savedObjects: { client: savedObjectsClient }, + } = await context.core; + + const [serviceSpecificDashboards] = await Promise.all([ + getServiceDashboards({ + savedObjectsClient, + serviceName, + }), + ]); + + return { serviceSpecificDashboards }; + }, +}); + export const serviceDashboardsRouteRepository = { ...serviceDashboardSaveRoute, + ...serviceDashboardsRoute, }; diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts index 7e287953d3854..04978da430010 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts @@ -22,7 +22,11 @@ export async function saveServiceDashbord({ serviceDashboardId, serviceDashboard, }: Options): Promise { - const { attributes, updated_at: updatedAt } = await (serviceDashboardId + const { + id, + attributes, + updated_at: updatedAt, + } = await (serviceDashboardId ? savedObjectsClient.update( APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, serviceDashboardId, @@ -33,6 +37,7 @@ export async function saveServiceDashbord({ serviceDashboard )); return { + id, ...(attributes as ServiceDashboard), updatedAt: updatedAt ? Date.parse(updatedAt) : 0, }; diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts index 9dacbb3d18a4c..45b05053b2ba4 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts @@ -16,8 +16,8 @@ export const apmServiceDashboards: SavedObjectsType = { namespaceType: 'multiple', mappings: { properties: { - id: { type: 'keyword' }, - title: { type: 'text' }, + dashboardSavedObjectId: { type: 'keyword' }, + dashboardTitle: { type: 'text' }, kuery: { type: 'text' }, serviceName: { type: 'keyword' }, environment: { type: 'keyword' }, @@ -36,8 +36,8 @@ export const apmServiceDashboards: SavedObjectsType = { changes: [], schemas: { create: schema.object({ - id: schema.string(), - title: schema.string(), + dashboardSavedObjectId: schema.string(), + dashboardTitle: schema.string(), kuery: schema.string(), serviceName: schema.maybe(schema.string()), environment: schema.maybe(schema.string()), From 8842051d243c737bc62b162fbf02c49a74c7b850 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 18 Sep 2023 11:53:26 +0200 Subject: [PATCH 07/61] Add linkTo property --- x-pack/plugins/apm/common/service_dashboards.ts | 14 ++++++++++++-- .../apm/public/hooks/use_dashboards_fetcher.ts | 13 ++++++------- .../apm/server/routes/service_dashboards/route.ts | 7 ++++--- .../server/saved_objects/apm_service_dashboards.ts | 8 ++++---- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index 2e691fee3baeb..ecbba6de4c4ab 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -7,12 +7,22 @@ export const APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE = 'apm-service-dashboard'; +// Define if the dashboard is linked to single or multiple services +export enum DashboardTypeEnum { + single = 'single', + multiple = 'multiple', +} + +export type DashboardType = + | DashboardTypeEnum.single + | DashboardTypeEnum.multiple; + export interface ServiceDashboard { dashboardSavedObjectId: string; dashboardTitle: string; - serviceName: string; - environment?: string; + useContextFilter: boolean; kuery: string; + linkTo: DashboardType; } export interface SavedServiceDashboard extends ServiceDashboard { diff --git a/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts index db31c771a0bd1..58bed3dbe3bcd 100644 --- a/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts @@ -9,26 +9,25 @@ import { useState, useEffect } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ApmPluginStartDeps } from '../plugin'; import { FETCH_STATUS } from './use_fetcher'; -import { DashboardItem } from '../../../../../src/plugins/dashboard/common/content_management'; +import { SearchDashboardsResponse } from '../../../../../src/plugins/dashboard/public/services/dashboard_content_management/lib/find_dashboards'; -export interface FetcherResult { - data?: DashboardItem[]; +export interface SearchDashboardsResult { + data: SearchDashboardsResponse['hits']; status: FETCH_STATUS; } -export function useDashboardFetcher(query?: string): FetcherResult { +export function useDashboardFetcher(query?: string): SearchDashboardsResult { const { services: { dashboard }, } = useKibana(); - const [result, setResult] = useState({ + const [result, setResult] = useState({ data: [], status: FETCH_STATUS.NOT_INITIATED, }); useEffect(() => { const getDashboards = async () => { - console.log('getDashboards'); setResult({ data: [], status: FETCH_STATUS.LOADING, @@ -41,7 +40,7 @@ export function useDashboardFetcher(query?: string): FetcherResult { }); setResult({ - data: data?.hits ?? [], + data: data.hits, status: FETCH_STATUS.SUCCESS, }); } catch { diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index d6339cb05da72..e5d5d24834981 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -10,6 +10,7 @@ import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { saveServiceDashbord } from './save_service_dashboard'; import { SavedServiceDashboard } from '../../../common/service_dashboards'; import { getServiceDashboards } from './get_service_dashboards'; +export const linkToRt = t.union([t.literal('single'), t.literal('multiple')]); const serviceDashboardSaveRoute = createApmServerRoute({ endpoint: 'POST /internal/apm/service-dashboard', @@ -23,9 +24,9 @@ const serviceDashboardSaveRoute = createApmServerRoute({ body: t.type({ dashboardSavedObjectId: t.string, dashboardTitle: t.string, - kuery: t.string, - serviceName: t.string, - environment: t.union([t.string, t.undefined]), + kuery: t.union([t.string, t.undefined]), + useContextFilter: t.boolean, + linkTo: linkToRt, }), }), options: { tags: ['access:apm', 'access:apm_write'] }, diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts index 45b05053b2ba4..32dd5d2501a50 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts @@ -19,8 +19,8 @@ export const apmServiceDashboards: SavedObjectsType = { dashboardSavedObjectId: { type: 'keyword' }, dashboardTitle: { type: 'text' }, kuery: { type: 'text' }, - serviceName: { type: 'keyword' }, - environment: { type: 'keyword' }, + useContextFilter: { type: 'boolean' }, + linkTo: { type: 'keyword' }, }, }, management: { @@ -39,8 +39,8 @@ export const apmServiceDashboards: SavedObjectsType = { dashboardSavedObjectId: schema.string(), dashboardTitle: schema.string(), kuery: schema.string(), - serviceName: schema.maybe(schema.string()), - environment: schema.maybe(schema.string()), + useContextFilter: schema.boolean(), + linkTo: schema.string(), }), }, }, From d7dfd7a24517da7ad220f08c0288a41f1ace351e Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 18 Sep 2023 11:54:03 +0200 Subject: [PATCH 08/61] wip --- .../plugins/apm/common/service_dashboards.ts | 3 +- .../actions/select_dashboard.tsx | 69 ++++++++++------- .../app/service_dashboards/index.tsx | 74 ++++++++++++++++--- .../get_service_dashboards.ts | 27 +++++-- .../server/routes/service_dashboards/route.ts | 24 ++++-- .../save_service_dashboard.ts | 12 ++- .../saved_objects/apm_service_dashboards.ts | 2 + 7 files changed, 157 insertions(+), 54 deletions(-) diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index ecbba6de4c4ab..07a509d4402bc 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -21,7 +21,8 @@ export interface ServiceDashboard { dashboardSavedObjectId: string; dashboardTitle: string; useContextFilter: boolean; - kuery: string; + kuery?: string; + serviceName?: string; linkTo: DashboardType; } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx index dbaf7e7859207..c10d42bc7368f 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx @@ -15,6 +15,8 @@ import { EuiSwitch, EuiModalBody, EuiComboBox, + EuiComboBoxOptionOption, + EuiFlexGroup, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { callApmApi } from '../../../../services/rest/create_call_apm_api'; @@ -22,6 +24,9 @@ import { useDashboardFetcher } from '../../../../hooks/use_dashboards_fetcher'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useApmParams } from '../../../../hooks/use_apm_params'; +import { DashboardItem } from '../../../../../../../../src/plugins/dashboard/common/content_management'; +import { DashboardTypeEnum } from '../../../../../common/service_dashboards'; +import { SERVICE_NAME } from '../../../../../common/es_fields/apm'; interface Props { onClose: () => void; @@ -33,7 +38,10 @@ export function SelectDashboard({ onClose }: Props) { } = useApmPluginContext(); const { data, status } = useDashboardFetcher(); - const [selectedDashboard, setSelectedDashboard] = useState([]); + const [useContextFilter, setUseContextFilter] = useState(true); + const [selectedDashboard, setSelectedDashboard] = useState< + Array> + >([]); const { path: { serviceName }, @@ -49,15 +57,14 @@ export function SelectDashboard({ onClose }: Props) { const onSave = useCallback( async function () { const [newDashboard] = selectedDashboard; - // setIsLoading(true); try { await callApmApi('POST /internal/apm/service-dashboard', { params: { body: { dashboardTitle: newDashboard.label, dashboardSavedObjectId: newDashboard.value, - kuery: '', - environment: '', + useContextFilter, + linkTo: DashboardTypeEnum.single, // serviceName, }, }, @@ -113,31 +120,37 @@ export function SelectDashboard({ onClose }: Props) { - ({ - label: dashboardItem.attributes.title, - value: dashboardItem.id, - }))} - selectedOptions={selectedDashboard} - onChange={(newSelection) => setSelectedDashboard(newSelection)} - isClearable={true} - /> + + ({ + label: dashboardItem.attributes.title, + value: dashboardItem.id, + }))} + selectedOptions={selectedDashboard} + onChange={(newSelection) => setSelectedDashboard(newSelection)} + isClearable={true} + /> - console.log('r')} - checked={false} - compressed - /> + setUseContextFilter(!useContextFilter)} + checked={useContextFilter} + /> + diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 19d68eabca7ed..829b3c642d832 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -4,23 +4,36 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; -import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useCallback, useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiComboBoxOptionOption, + EuiComboBox, +} from '@elastic/eui'; import { EmptyDashboards } from './empty_dashboards'; import { AddDashboard } from './actions'; import { useFetcher } from '../../../hooks/use_fetcher'; import { useApmParams } from '../../../hooks/use_apm_params'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { AwaitingDashboardAPI, + DashboardCreationOptions, DashboardRenderer, } from '@kbn/dashboard-plugin/public'; +import { SERVICE_NAME } from '../../../../common/es_fields/apm'; export function ServiceDashboards() { const { path: { serviceName }, query: { environment, kuery, rangeFrom, rangeTo }, } = useApmParams('/services/{serviceName}/dashboards'); + const [dashboard, setDashboard] = useState(); + const [selectedDashboard, setSelectedDashboard] = + useState>(); const { data, status } = useFetcher( (callApmApi) => { @@ -40,21 +53,62 @@ export function ServiceDashboards() { console.log('data====', data); + const getCreationOptions = + useCallback((): Promise => { + const getInitialInput = () => ({ + viewMode: ViewMode.VIEW, + timeRange: { from: rangeFrom, to: rangeTo }, + query: { query: kuery, language: 'kuery' }, + }); + return Promise.resolve({ getInitialInput }); + }, [rangeFrom, rangeTo, kuery]); + + useEffect(() => { + if (!dashboard) return; + console.log('update'); + dashboard.updateInput({ + viewMode: ViewMode.VIEW, + timeRange: { from: rangeFrom, to: rangeTo }, + query: { query: kuery, language: 'kuery' }, + }); + }, [kuery, serviceName, environment, rangeFrom, rangeTo, selectedDashboard]); + + console.log('///selectedDashboard', selectedDashboard); return ( - Title Placeholder + Custom - // TODO add loading - {data && data?.serviceSpecificDashboards ? ( - + + {data && selectedDashboard ? ( + <> + ({ + label: dashboardItem.dashboardTitle, + value: dashboardItem.dashboardSavedObjectId, + }) + )} + selectedOptions={selectedDashboard} + onChange={(newSelection) => setSelectedDashboard(newSelection)} + isClearable={true} + /> + + ) : ( } /> )} diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts index 1429559de7e9c..13a333e6c7baf 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts @@ -6,6 +6,8 @@ */ import { SavedObjectsClientContract } from '@kbn/core/server'; +import { fromKueryExpression } from '@kbn/es-query/src/kuery/ast/ast'; +import { SERVICE_NAME } from '../../../common/es_fields/apm'; import { APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, SavedServiceDashboard, @@ -14,21 +16,36 @@ import { interface Props { savedObjectsClient: SavedObjectsClientContract; - serviceName: string; + query: string; } export async function getServiceDashboards({ savedObjectsClient, - serviceName, + query, }: Props): Promise { - // savedObjectsClient.bulkDelete(APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE); const result = await savedObjectsClient.find({ type: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, page: 1, - perPage: 10, + perPage: 100, + filter: query, }); - console.log('result====', result); + const all = await savedObjectsClient.find({ + type: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + page: 1, + perPage: 100, + }); + + const allAttibutes = all.saved_objects.map( + ({ id, attributes, updated_at: upatedAt }) => ({ + id, + updatedAt: upatedAt ? Date.parse(upatedAt) : 0, + ...attributes, + }) + ); + + console.log('all====', allAttibutes); + // const opt = result.saved_objects.map( // ({ id, type, attributes, updated_at: upatedAt }) => ({ type, id }) // ); diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index e5d5d24834981..6236e2a849647 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -8,9 +8,18 @@ import * as t from 'io-ts'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { saveServiceDashbord } from './save_service_dashboard'; -import { SavedServiceDashboard } from '../../../common/service_dashboards'; +import { + APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + DashboardTypeEnum, + SavedServiceDashboard, +} from '../../../common/service_dashboards'; import { getServiceDashboards } from './get_service_dashboards'; -export const linkToRt = t.union([t.literal('single'), t.literal('multiple')]); +import { fromKueryExpression } from '../../../../../../packages/kbn-es-query'; + +const linkToRt = t.union([ + t.literal(DashboardTypeEnum.single), + t.literal(DashboardTypeEnum.multiple), +]); const serviceDashboardSaveRoute = createApmServerRoute({ endpoint: 'POST /internal/apm/service-dashboard', @@ -25,6 +34,7 @@ const serviceDashboardSaveRoute = createApmServerRoute({ dashboardSavedObjectId: t.string, dashboardTitle: t.string, kuery: t.union([t.string, t.undefined]), + serviceName: t.union([t.string, t.undefined]), useContextFilter: t.boolean, linkTo: linkToRt, }), @@ -57,22 +67,24 @@ const serviceDashboardsRoute = createApmServerRoute({ }, handler: async ( resources - ): Promise<{ serviceSpecificDashboards: SavedServiceDashboard[] }> => { + ): Promise<{ serviceDashboards: SavedServiceDashboard[] }> => { const { context, params } = resources; const { serviceName } = params.path; + const so_prefix_attributes = `${APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE}.attributes`; const { savedObjects: { client: savedObjectsClient }, } = await context.core; - const [serviceSpecificDashboards] = await Promise.all([ + const [serviceDashboards] = await Promise.all([ getServiceDashboards({ savedObjectsClient, - serviceName, + query: `${so_prefix_attributes}.kuery: "service.name\: ${serviceName}"`, + // query: `'service.name: ${serviceName}' | single`, }), ]); - return { serviceSpecificDashboards }; + return { serviceDashboards }; }, }); diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts index 04978da430010..9eba51f58d644 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts @@ -22,6 +22,13 @@ export async function saveServiceDashbord({ serviceDashboardId, serviceDashboard, }: Options): Promise { + const t = { + dashboardSavedObjectId: 'id-2', + dashboardTitle: 'title-2', + useContextFilter: false, + kuery: 'link to', + linkTo: 'single', + }; const { id, attributes, @@ -32,10 +39,7 @@ export async function saveServiceDashbord({ serviceDashboardId, serviceDashboard ) - : savedObjectsClient.create( - APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, - serviceDashboard - )); + : savedObjectsClient.create(APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, t)); return { id, ...(attributes as ServiceDashboard), diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts index 32dd5d2501a50..9feb9011ec346 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts @@ -21,6 +21,7 @@ export const apmServiceDashboards: SavedObjectsType = { kuery: { type: 'text' }, useContextFilter: { type: 'boolean' }, linkTo: { type: 'keyword' }, + serviceName: { type: 'keyword' }, }, }, management: { @@ -41,6 +42,7 @@ export const apmServiceDashboards: SavedObjectsType = { kuery: schema.string(), useContextFilter: schema.boolean(), linkTo: schema.string(), + serviceName: schema.maybe(schema.string()), }), }, }, From 3a831f202347d875383841220a6ec462a58b77f9 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 18 Sep 2023 12:48:44 +0200 Subject: [PATCH 09/61] Fetch dashboard specific to the service --- .../plugins/apm/common/service_dashboards.ts | 2 +- .../actions/select_dashboard.tsx | 4 +-- .../get_service_dashboards.ts | 30 ++----------------- .../server/routes/service_dashboards/route.ts | 7 +++-- .../save_service_dashboard.ts | 12 +++----- .../saved_objects/apm_service_dashboards.ts | 4 +-- 6 files changed, 16 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index 07a509d4402bc..9a66883422eb6 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -22,7 +22,7 @@ export interface ServiceDashboard { dashboardTitle: string; useContextFilter: boolean; kuery?: string; - serviceName?: string; + serviceName: string; linkTo: DashboardType; } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx index c10d42bc7368f..eaf1b76e026df 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard.tsx @@ -62,9 +62,9 @@ export function SelectDashboard({ onClose }: Props) { params: { body: { dashboardTitle: newDashboard.label, - dashboardSavedObjectId: newDashboard.value, + dashboardSavedObjectId: newDashboard?.value, useContextFilter, - linkTo: DashboardTypeEnum.single, // + linkTo: DashboardTypeEnum.single, // iteration-1: Only single supported serviceName, }, }, diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts index 13a333e6c7baf..136109a17e70a 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts @@ -6,8 +6,6 @@ */ import { SavedObjectsClientContract } from '@kbn/core/server'; -import { fromKueryExpression } from '@kbn/es-query/src/kuery/ast/ast'; -import { SERVICE_NAME } from '../../../common/es_fields/apm'; import { APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, SavedServiceDashboard, @@ -16,42 +14,20 @@ import { interface Props { savedObjectsClient: SavedObjectsClientContract; - query: string; + filter: string; } export async function getServiceDashboards({ savedObjectsClient, - query, + filter, }: Props): Promise { const result = await savedObjectsClient.find({ type: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, page: 1, perPage: 100, - filter: query, + filter, }); - const all = await savedObjectsClient.find({ - type: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, - page: 1, - perPage: 100, - }); - - const allAttibutes = all.saved_objects.map( - ({ id, attributes, updated_at: upatedAt }) => ({ - id, - updatedAt: upatedAt ? Date.parse(upatedAt) : 0, - ...attributes, - }) - ); - - console.log('all====', allAttibutes); - - // const opt = result.saved_objects.map( - // ({ id, type, attributes, updated_at: upatedAt }) => ({ type, id }) - // ); - - // savedObjectsClient.bulkDelete(opt); - return result.saved_objects.map( ({ id, attributes, updated_at: upatedAt }) => ({ id, diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index 6236e2a849647..9dcce9c9199a6 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -70,7 +70,9 @@ const serviceDashboardsRoute = createApmServerRoute({ ): Promise<{ serviceDashboards: SavedServiceDashboard[] }> => { const { context, params } = resources; const { serviceName } = params.path; - const so_prefix_attributes = `${APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE}.attributes`; + + const soPrefixServiceName = `${APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE}.attributes.serviceName`; + const soPrefixLinkTo = `${APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE}.attributes.linkTo`; const { savedObjects: { client: savedObjectsClient }, @@ -79,8 +81,7 @@ const serviceDashboardsRoute = createApmServerRoute({ const [serviceDashboards] = await Promise.all([ getServiceDashboards({ savedObjectsClient, - query: `${so_prefix_attributes}.kuery: "service.name\: ${serviceName}"`, - // query: `'service.name: ${serviceName}' | single`, + filter: `${soPrefixServiceName}:${serviceName} AND ${soPrefixLinkTo}: ${DashboardTypeEnum.single} `, }), ]); diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts index 9eba51f58d644..04978da430010 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts @@ -22,13 +22,6 @@ export async function saveServiceDashbord({ serviceDashboardId, serviceDashboard, }: Options): Promise { - const t = { - dashboardSavedObjectId: 'id-2', - dashboardTitle: 'title-2', - useContextFilter: false, - kuery: 'link to', - linkTo: 'single', - }; const { id, attributes, @@ -39,7 +32,10 @@ export async function saveServiceDashbord({ serviceDashboardId, serviceDashboard ) - : savedObjectsClient.create(APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, t)); + : savedObjectsClient.create( + APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + serviceDashboard + )); return { id, ...(attributes as ServiceDashboard), diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts index 9feb9011ec346..3d79f3eca9a2a 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts @@ -39,10 +39,10 @@ export const apmServiceDashboards: SavedObjectsType = { create: schema.object({ dashboardSavedObjectId: schema.string(), dashboardTitle: schema.string(), - kuery: schema.string(), + kuery: schema.maybe(schema.string()), useContextFilter: schema.boolean(), linkTo: schema.string(), - serviceName: schema.maybe(schema.string()), + serviceName: schema.string(), }), }, }, From bcc0879b5081f9eb22dabd93589fb489edd68179 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 19 Sep 2023 09:08:44 +0200 Subject: [PATCH 10/61] Add contect menu --- .../actions/add_dashboard.tsx | 2 +- .../app/service_dashboards/context_menu.tsx | 148 +++++++++++++++++ .../dashboards_dropdown_list.tsx | 3 +- .../service_dashboards/empty_dashboards.tsx | 112 +++++++------ .../app/service_dashboards/index.tsx | 154 +++++++++++++----- 5 files changed, 330 insertions(+), 89 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx index 910528a77a3fa..2744328a34d56 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx @@ -19,7 +19,7 @@ export function AddDashboard() { onClick={() => setIsModalVisible(true)} > {i18n.translate('xpack.apm.serviceDashboards.addButtonLabel', { - defaultMessage: 'Add dashboard', + defaultMessage: 'Link dashboard', })} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx new file mode 100644 index 0000000000000..dc13cc399429e --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useState } from 'react'; +import { + EuiButton, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiContextMenuPanel, + EuiContextMenuItem, + EuiPopover, + EuiComboBox, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { SavedServiceDashboard } from '../../../../common/service_dashboards'; + +type Props = { + serviceDashboards: SavedServiceDashboard[]; + selectedDashboard: SavedServiceDashboard; + handleOnChange: (selectedId: string) => void; +}; + +export function ContextMenu({ + serviceDashboards, + selectedDashboard, + handleOnChange, +}: Props) { + const [isPopoverOpen, setPopover] = useState(false); + + const onButtonClick = () => { + setPopover(!isPopoverOpen); + }; + + const closePopover = () => { + setPopover(false); + }; + + const options: Array> = [ + ...serviceDashboards.map(({ dashboardSavedObjectId, dashboardTitle }) => { + return { label: dashboardTitle, value: dashboardSavedObjectId }; + }), + ]; + + useEffect(() => { + if (!selectedDashboard && serviceDashboards.length > 0) { + console.log('if- contect menu - serviceDashboards', serviceDashboards); + console.log('if- contect menu - selectedDashboard', selectedDashboard); + const [serviceDashboard] = serviceDashboards; + console.log('first service dashboard', serviceDashboard); + handleOnChange(serviceDashboard.dashboardSavedObjectId); + } + }, [selectedDashboard, serviceDashboards]); + + const items = [ + + {i18n.translate('xpack.apm.serviceDashboards.contextMenu.linkDashboard', { + defaultMessage: 'Link new dashboard', + })} + , + + {i18n.translate('xpack.apm.serviceDashboards.contextMenu.goToDashboard', { + defaultMessage: 'Go to dashboard', + })} + , + + {i18n.translate( + 'xpack.apm.serviceDashboards.contextMenu.visGaugeDashboard', + { + defaultMessage: 'Edit dashboard link', + } + )} + , + + {i18n.translate( + 'xpack.apm.serviceDashboards.contextMenu.unlinkDashboard', + { + defaultMessage: 'Unlink dashboard', + } + )} + , + ]; + + return ( + <> + + + { + handleOnChange(newItem.value)} + /> + } + + + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx index 3613a9efcb721..fa4b3fb99e151 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx @@ -11,9 +11,8 @@ import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; -export function DashboardsDropdownList(selectedDashboard, onChangeDashboard) { +export function DashboardsDropdownList() { const { data, status } = useDashboardFetcher(); - const [selectedOptions, setSelected] = useState([]); const onChange = ( selectedOptions: Array> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx index 1283699c75b2e..c2fc38a1f3882 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx @@ -5,7 +5,12 @@ * 2.0. */ import React from 'react'; -import { EuiEmptyPrompt, EuiImage } from '@elastic/eui'; +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiImage, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg'; import { useTheme } from '../../../hooks/use_theme'; @@ -16,55 +21,68 @@ interface Props { export function EmptyDashboards({ actions }: Props) { const theme = useTheme(); + return ( - - } - title={ -

- {i18n.translate('xpack.apm.serviceDashboards.emptyTitle', { - defaultMessage: - 'The best way to understand your data is to visualize it.', + <> + + + {i18n.translate('xpack.apm.serviceDashboards.selectDashboard.title', { + defaultMessage: 'Custom', })} -

- } - layout="horizontal" - color="plain" - body={ - <> -
    -
  • - {i18n.translate('xpack.apm.serviceDashboards.emptyBody.first', { - defaultMessage: 'bring clarity to your data', - })} -
  • -
  • - {i18n.translate('xpack.apm.serviceDashboards.emptyBody.second', { - defaultMessage: 'tell a story about your data', - })} -
  • -
  • - {i18n.translate('xpack.apm.serviceDashboards.emptyBody', { - defaultMessage: - 'focus on only the data that’s important to you', - })} -
  • -
-

+ + + + } + title={ +

{i18n.translate('xpack.apm.serviceDashboards.emptyTitle', { - defaultMessage: 'To get started, add your dashaboard', + defaultMessage: + 'The best way to understand your data is to visualize it.', })} -

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

+ } + layout="horizontal" + color="plain" + body={ + <> +
    +
  • + {i18n.translate('xpack.apm.serviceDashboards.emptyBody.first', { + defaultMessage: 'bring clarity to your data', + })} +
  • +
  • + {i18n.translate( + 'xpack.apm.serviceDashboards.emptyBody.second', + { + defaultMessage: 'tell a story about your data', + } + )} +
  • +
  • + {i18n.translate('xpack.apm.serviceDashboards.emptyBody', { + defaultMessage: + 'focus on only the data that’s important to you', + })} +
  • +
+

+ {i18n.translate('xpack.apm.serviceDashboards.emptyTitle', { + defaultMessage: 'To get started, add your dashaboard', + })} +

+ + } + actions={actions} + /> + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 829b3c642d832..378c494f3545a 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -6,6 +6,7 @@ */ import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; +import { buildPhraseFilter, Filter, TimeRange } from '@kbn/es-query'; import { EuiPanel, EuiFlexGroup, @@ -16,7 +17,7 @@ import { import { EmptyDashboards } from './empty_dashboards'; import { AddDashboard } from './actions'; -import { useFetcher } from '../../../hooks/use_fetcher'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useApmParams } from '../../../hooks/use_apm_params'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { @@ -24,7 +25,8 @@ import { DashboardCreationOptions, DashboardRenderer, } from '@kbn/dashboard-plugin/public'; -import { SERVICE_NAME } from '../../../../common/es_fields/apm'; +import { SavedServiceDashboard } from '../../../../common/service_dashboards'; +import { ContextMenu } from './context_menu'; export function ServiceDashboards() { const { @@ -33,7 +35,7 @@ export function ServiceDashboards() { } = useApmParams('/services/{serviceName}/dashboards'); const [dashboard, setDashboard] = useState(); const [selectedDashboard, setSelectedDashboard] = - useState>(); + useState(); const { data, status } = useFetcher( (callApmApi) => { @@ -51,63 +53,137 @@ export function ServiceDashboards() { [serviceName] ); - console.log('data====', data); + // function getFilters( + // serviceName: string, + // environment: string, + // ): Filter[] { + + // const filter = []; + + // const environmentField = dataView.getFieldByName(SERVICE_ENVIRONMENT); + // if ( + // environmentField && + // !!environment && + // environment !== ENVIRONMENT_ALL.value && + // environment !== ENVIRONMENT_NOT_DEFINED.value + // ) { + // const environmentFilter = buildPhraseFilter( + // environmentField, + // environment, + // ); + // filter.push(environmentFilter); + // } + + // const serviceNameField = dataView.getFieldByName(SERVICE_NAME); + // if (serviceNameField) { + // const serviceNameFilter = buildPhraseFilter( + // serviceNameField, + // serviceName, + // dataView + // ); + // filter.push(serviceNameFilter); + // } + + // return filter; + // } const getCreationOptions = useCallback((): Promise => { + console.log('selectedDashboard', selectedDashboard); const getInitialInput = () => ({ viewMode: ViewMode.VIEW, timeRange: { from: rangeFrom, to: rangeTo }, query: { query: kuery, language: 'kuery' }, }); return Promise.resolve({ getInitialInput }); - }, [rangeFrom, rangeTo, kuery]); + }, [rangeFrom, rangeTo, kuery, selectedDashboard]); + + const serviceDashboards = data?.serviceDashboards ?? []; useEffect(() => { if (!dashboard) return; - console.log('update'); + dashboard.updateInput({ viewMode: ViewMode.VIEW, timeRange: { from: rangeFrom, to: rangeTo }, + // TODO useContextFilter query: { query: kuery, language: 'kuery' }, }); - }, [kuery, serviceName, environment, rangeFrom, rangeTo, selectedDashboard]); + }, [ + serviceDashboards, + kuery, + serviceName, + environment, + rangeFrom, + rangeTo, + selectedDashboard, + ]); + + const handleOnChange = (selectedId: string) => { + setSelectedDashboard( + serviceDashboards.find( + ({ dashboardSavedObjectId }) => dashboardSavedObjectId === selectedId + ) + ); + }; - console.log('///selectedDashboard', selectedDashboard); return ( - - Custom - - - - - - {data && selectedDashboard ? ( + {status !== FETCH_STATUS.LOADING && serviceDashboards.length > 0 ? ( <> - ({ - label: dashboardItem.dashboardTitle, - value: dashboardItem.dashboardSavedObjectId, - }) - )} - selectedOptions={selectedDashboard} - onChange={(newSelection) => setSelectedDashboard(newSelection)} - isClearable={true} - /> - + + + {selectedDashboard?.dashboardTitle} + + + {/* + setSelectedDashboard( + serviceDashboards.find( + ({ dashboardSavedObjectId }) => + dashboardSavedObjectId === newSelectedDashboard.value + ) + ) + } + isClearable={true} + /> */} + + + + + {selectedDashboard && ( + + )} ) : ( } /> From 4e29c2d32089f8ae2be658824c4cf962594afed5 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 19 Sep 2023 15:17:33 +0200 Subject: [PATCH 11/61] Fix context menu --- .../actions/add_dashboard.tsx | 12 +- ...shboard.tsx => select_dashboard_modal.tsx} | 3 +- .../app/service_dashboards/context_menu.tsx | 106 +++++++++++------- .../dashboards_dropdown_list.tsx | 43 ------- .../app/service_dashboards/index.tsx | 87 +++----------- 5 files changed, 90 insertions(+), 161 deletions(-) rename x-pack/plugins/apm/public/components/app/service_dashboards/actions/{select_dashboard.tsx => select_dashboard_modal.tsx} (98%) delete mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx index 2744328a34d56..f922b88fdbefc 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx @@ -7,11 +7,15 @@ import { EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { SelectDashboard } from './select_dashboard'; - -export function AddDashboard() { - const [isModalVisible, setIsModalVisible] = useState(false); +import { SelectDashboard } from './select_dashboard_modal'; +export function AddDashboard({ + isModalVisible, + setIsModalVisible, +}: { + isModalVisible: boolean; + setIsModalVisible: (isVisible: boolean) => void; +}) { return ( <> void; @@ -164,7 +163,7 @@ export function SelectDashboard({ onClose }: Props) { {i18n.translate('xpack.apm.serviceDashboards.selectDashboard.add', { - defaultMessage: 'Add dashboard', + defaultMessage: 'Link dashboard', })} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx index dc13cc399429e..252ce97ef65e6 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { EuiButton, EuiButtonIcon, @@ -8,9 +9,13 @@ import { EuiContextMenuItem, EuiPopover, EuiComboBox, + EuiContextMenu, + EuiIcon, + EuiComboBoxOptionOption, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { SavedServiceDashboard } from '../../../../common/service_dashboards'; +import { ApmPluginStartDeps } from '../../../plugin'; type Props = { serviceDashboards: SavedServiceDashboard[]; @@ -23,6 +28,12 @@ export function ContextMenu({ selectedDashboard, handleOnChange, }: Props) { + const { + services: { + dashboard: { locator: dashboardLocator }, + }, + } = useKibana(); + const [isPopoverOpen, setPopover] = useState(false); const onButtonClick = () => { @@ -41,50 +52,66 @@ export function ContextMenu({ useEffect(() => { if (!selectedDashboard && serviceDashboards.length > 0) { - console.log('if- contect menu - serviceDashboards', serviceDashboards); - console.log('if- contect menu - selectedDashboard', selectedDashboard); const [serviceDashboard] = serviceDashboards; - console.log('first service dashboard', serviceDashboard); handleOnChange(serviceDashboard.dashboardSavedObjectId); } }, [selectedDashboard, serviceDashboards]); - const items = [ - - {i18n.translate('xpack.apm.serviceDashboards.contextMenu.linkDashboard', { - defaultMessage: 'Link new dashboard', - })} - , - - {i18n.translate('xpack.apm.serviceDashboards.contextMenu.goToDashboard', { - defaultMessage: 'Go to dashboard', - })} - , - - {i18n.translate( - 'xpack.apm.serviceDashboards.contextMenu.visGaugeDashboard', + const panels = [ + { + id: 0, + title: '', + items: [ + { + name: i18n.translate( + 'xpack.apm.serviceDashboards.contextMenu.linkDashboard', + { + defaultMessage: 'Link new dashboard', + } + ), + icon: 'plusInCircle', + onClick: () => { + closePopover(); + }, + }, { - defaultMessage: 'Edit dashboard link', - } - )} - , - - {i18n.translate( - 'xpack.apm.serviceDashboards.contextMenu.unlinkDashboard', + name: i18n.translate( + 'xpack.apm.serviceDashboards.contextMenu.goToDashboard', + { + defaultMessage: 'Go to dashboard', + } + ), + icon: 'visGauge', + href: dashboardLocator?.getRedirectUrl({ + dashboardId: selectedDashboard?.dashboardSavedObjectId, + }), + }, { - defaultMessage: 'Unlink dashboard', - } - )} - , + name: i18n.translate( + 'xpack.apm.serviceDashboards.contextMenu.visGaugeDashboard', + { + defaultMessage: 'Edit dashboard link', + } + ), + icon: 'pencil', + onClick: () => { + closePopover(); + }, + }, + { + name: i18n.translate( + 'xpack.apm.serviceDashboards.contextMenu.unlinkDashboard', + { + defaultMessage: 'Unlink dashboard', + } + ), + icon: 'unlink', + onClick: () => { + closePopover(); + }, + }, + ], + }, ]; return ( @@ -120,6 +147,7 @@ export function ContextMenu({ : [] } onChange={([newItem]) => handleOnChange(newItem.value)} + isClearable={false} /> } @@ -139,7 +167,7 @@ export function ContextMenu({ panelPaddingSize="none" anchorPosition="downLeft" > - + diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx deleted file mode 100644 index fa4b3fb99e151..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboards_dropdown_list.tsx +++ /dev/null @@ -1,43 +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 { i18n } from '@kbn/i18n'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; -import { FETCH_STATUS } from '../../../hooks/use_fetcher'; - -export function DashboardsDropdownList() { - const { data, status } = useDashboardFetcher(); - - const onChange = ( - selectedOptions: Array> - ) => { - onChangeDashboard(selectedOptions); - }; - - return ( - ({ - label: dashboardItem.attributes.title, - value: dashboardItem.id, - }))} - selectedOptions={selectedDashboard} - onChange={onChange} - isClearable={true} - /> - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 378c494f3545a..e0ebda0b58196 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -36,6 +36,7 @@ export function ServiceDashboards() { const [dashboard, setDashboard] = useState(); const [selectedDashboard, setSelectedDashboard] = useState(); + const [isModalVisible, setIsModalVisible] = useState(false); const { data, status } = useFetcher( (callApmApi) => { @@ -53,40 +54,6 @@ export function ServiceDashboards() { [serviceName] ); - // function getFilters( - // serviceName: string, - // environment: string, - // ): Filter[] { - - // const filter = []; - - // const environmentField = dataView.getFieldByName(SERVICE_ENVIRONMENT); - // if ( - // environmentField && - // !!environment && - // environment !== ENVIRONMENT_ALL.value && - // environment !== ENVIRONMENT_NOT_DEFINED.value - // ) { - // const environmentFilter = buildPhraseFilter( - // environmentField, - // environment, - // ); - // filter.push(environmentFilter); - // } - - // const serviceNameField = dataView.getFieldByName(SERVICE_NAME); - // if (serviceNameField) { - // const serviceNameFilter = buildPhraseFilter( - // serviceNameField, - // serviceName, - // dataView - // ); - // filter.push(serviceNameFilter); - // } - - // return filter; - // } - const getCreationOptions = useCallback((): Promise => { console.log('selectedDashboard', selectedDashboard); @@ -136,45 +103,12 @@ export function ServiceDashboards() { {selectedDashboard?.dashboardTitle} - {/* - setSelectedDashboard( - serviceDashboards.find( - ({ dashboardSavedObjectId }) => - dashboardSavedObjectId === newSelectedDashboard.value - ) - ) - } - isClearable={true} - /> */} + - {selectedDashboard && ( @@ -186,7 +120,14 @@ export function ServiceDashboards() { )} ) : ( - } /> + + } + /> )} ); From 01896c7794aad9f04e06a7651811881746226b54 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 19 Sep 2023 15:27:36 +0200 Subject: [PATCH 12/61] Fix refresh --- .../app/service_dashboards/actions/add_dashboard.tsx | 9 +++++++-- .../actions/select_dashboard_modal.tsx | 6 +++--- .../public/components/app/service_dashboards/index.tsx | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx index f922b88fdbefc..94c1b13f03d17 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx @@ -6,15 +6,17 @@ */ import { EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useState } from 'react'; +import React from 'react'; import { SelectDashboard } from './select_dashboard_modal'; export function AddDashboard({ isModalVisible, setIsModalVisible, + onRefresh, }: { isModalVisible: boolean; setIsModalVisible: (isVisible: boolean) => void; + onRefresh: () => void; }) { return ( <> @@ -28,7 +30,10 @@ export function AddDashboard({
{isModalVisible && ( - setIsModalVisible(false)} /> + setIsModalVisible(false)} + onRefresh={onRefresh} + /> )} ); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx index 3e7108989ae36..528f5b6af6f6e 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx @@ -31,7 +31,7 @@ interface Props { onClose: () => void; } -export function SelectDashboard({ onClose }: Props) { +export function SelectDashboard({ onClose, onRefresh }: Props) { const { core: { notifications }, } = useApmPluginContext(); @@ -50,8 +50,8 @@ export function SelectDashboard({ onClose }: Props) { // TODO need to refetch and not reload const reloadServiceDashboards = useCallback(() => { - window.location.reload(); - }, []); + onRefresh(); + }, [onRefresh]); const onSave = useCallback( async function () { diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index e0ebda0b58196..3d8ed0c49aff4 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -38,7 +38,7 @@ export function ServiceDashboards() { useState(); const [isModalVisible, setIsModalVisible] = useState(false); - const { data, status } = useFetcher( + const { data, status, refetch } = useFetcher( (callApmApi) => { if (serviceName) { return callApmApi( @@ -56,7 +56,6 @@ export function ServiceDashboards() { const getCreationOptions = useCallback((): Promise => { - console.log('selectedDashboard', selectedDashboard); const getInitialInput = () => ({ viewMode: ViewMode.VIEW, timeRange: { from: rangeFrom, to: rangeTo }, @@ -125,6 +124,7 @@ export function ServiceDashboards() { } /> From 007011765e41e68f1f84b19abba645feedefbac6 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 19 Sep 2023 23:26:43 +0200 Subject: [PATCH 13/61] Add unlink option --- .../actions/unlink_dashboard.tsx | 107 ++++++++++++++++++ .../remove_service_dashboard.ts | 23 ++++ 2 files changed, 130 insertions(+) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx create mode 100644 x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx new file mode 100644 index 0000000000000..b461be6d839c3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiButtonEmpty, EuiConfirmModal } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useState } from 'react'; +import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { callApmApi } from '../../../../services/rest/create_call_apm_api'; + +export function UnlinkDashboard({ + selectedDashboard, + onRefresh, +}: { + selectedDashboard: SavedServiceDashboard; + onRefresh: () => void; +}) { + const [isModalVisible, setIsModalVisible] = useState(false); + const { + core: { notifications }, + } = useApmPluginContext(); + + const onConfirm = useCallback( + async function () { + try { + await callApmApi('DELETE /internal/apm/service-dashboard', { + params: { query: { serviceDashboardId: selectedDashboard.id } }, + signal: null, + }); + + notifications.toasts.addSuccess({ + title: i18n.translate( + 'xpack.apm.serviceDashboards.unlinkSuccess.toast.title', + { + defaultMessage: 'Unlinked "{dashboardName}" dashboard', + values: { dashboardName: selectedDashboard.dashboardTitle }, + } + ), + }); + onRefresh(); + } catch (error) { + console.error(error); + notifications.toasts.addDanger({ + title: i18n.translate( + 'xpack.apm.serviceDashboards.unlinkFailure.toast.title', + { + defaultMessage: + 'Error while unlinking "{dashboardName}" dashboard', + values: { dashboardName: selectedDashboard.dashboardTitle }, + } + ), + text: error.body.message, + }); + } + setIsModalVisible(!isModalVisible); + }, + [selectedDashboard, notifications.toasts] + ); + return ( + <> + setIsModalVisible(true)} + > + {i18n.translate('xpack.apm.serviceDashboards.unlinkEmptyButtonLabel', { + defaultMessage: 'Unlink dashboard', + })} + + {isModalVisible && ( + setIsModalVisible(false)} + onConfirm={onConfirm} + confirmButtonText={i18n.translate( + 'xpack.apm.serviceDashboards.unlinkEmptyButtonLabel.confirm.button', + { + defaultMessage: 'Unlink dashboard', + } + )} + buttonColor="danger" + defaultFocusedButton="confirm" + > +

+ {i18n.translate( + 'xpack.apm.serviceDashboards.unlinkEmptyButtonLabel.confirm.body', + { + defaultMessage: + 'You are about to unlink the dashboard from the service context', + } + )} +

+
+ )} + + ); +} diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts b/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts new file mode 100644 index 0000000000000..9a2250a11f325 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE } from '../../../common/service_dashboards'; + +interface Options { + savedObjectsClient: SavedObjectsClientContract; + serviceDashboardId: string; +} +export async function deleteServiceDashboard({ + savedObjectsClient, + serviceDashboardId, +}: Options) { + return savedObjectsClient.delete( + APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + serviceDashboardId + ); +} From 776c330c79e48220359afd9c23ec017490f34a33 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 19 Sep 2023 23:27:03 +0200 Subject: [PATCH 14/61] Go to dashboard link --- .../actions/goto_dashboard.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx new file mode 100644 index 0000000000000..72c0d22c1f9a3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.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 { EuiButton, EuiButtonEmpty } from '@elastic/eui'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { ApmPluginStartDeps } from '../../../../plugin'; +import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; + +export function GotoDashboard({ + selectedDashboard, +}: { + selectedDashboard: SavedServiceDashboard; +}) { + const { + services: { + dashboard: { locator: dashboardLocator }, + }, + } = useKibana(); + + const url = dashboardLocator?.getRedirectUrl({ + dashboardId: selectedDashboard?.dashboardSavedObjectId, + }); + return ( + + {i18n.translate('xpack.apm.serviceDashboards.contextMenu.goToDashboard', { + defaultMessage: 'Go to dashboard', + })} + + ); +} From 38a2d9183ac6cdd677f80399e218c678637ba4b3 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 19 Sep 2023 23:27:23 +0200 Subject: [PATCH 15/61] Additional actions --- .../{add_dashboard.tsx => edit_dashboard.tsx} | 17 ++-- .../app/service_dashboards/actions/index.ts | 6 +- .../actions/link_dashboard.tsx | 56 ++++++++++++ .../actions/select_dashboard_modal.tsx | 1 - .../app/service_dashboards/context_menu.tsx | 85 ++++--------------- .../app/service_dashboards/index.tsx | 30 ++++--- .../server/routes/service_dashboards/route.ts | 22 ++++- 7 files changed, 125 insertions(+), 92 deletions(-) rename x-pack/plugins/apm/public/components/app/service_dashboards/actions/{add_dashboard.tsx => edit_dashboard.tsx} (68%) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx similarity index 68% rename from x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx rename to x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx index 94c1b13f03d17..8be2d1633f529 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/add_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx @@ -4,12 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiButton } from '@elastic/eui'; +import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { SelectDashboard } from './select_dashboard_modal'; -export function AddDashboard({ +export function EditDashboard({ isModalVisible, setIsModalVisible, onRefresh, @@ -20,14 +20,17 @@ export function AddDashboard({ }) { return ( <> - setIsModalVisible(true)} > - {i18n.translate('xpack.apm.serviceDashboards.addButtonLabel', { - defaultMessage: 'Link dashboard', + {i18n.translate('xpack.apm.serviceDashboards.editEmptyButtonLabel', { + defaultMessage: 'Edit dashboard', })} - + {isModalVisible && ( void; + onRefresh: () => void; + emptyButton?: Boolean; +}) { + return ( + <> + {emptyButton ? ( + setIsModalVisible(true)} + > + {i18n.translate('xpack.apm.serviceDashboards.linkEmptyButtonLabel', { + defaultMessage: 'Link new dashboard', + })} + + ) : ( + setIsModalVisible(true)} + > + {i18n.translate('xpack.apm.serviceDashboards.linkButtonLabel', { + defaultMessage: 'Link dashboard', + })} + + )} + + {isModalVisible && ( + setIsModalVisible(false)} + onRefresh={onRefresh} + /> + )} + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx index 528f5b6af6f6e..4e1f62982ecc8 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx @@ -48,7 +48,6 @@ export function SelectDashboard({ onClose, onRefresh }: Props) { console.log('selectedDashboard', selectedDashboard); - // TODO need to refetch and not reload const reloadServiceDashboards = useCallback(() => { onRefresh(); }, [onRefresh]); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx index 252ce97ef65e6..228d4de1ac04b 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx @@ -1,7 +1,5 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; +import React, { useEffect, useState } from 'react'; import { - EuiButton, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, @@ -9,31 +7,30 @@ import { EuiContextMenuItem, EuiPopover, EuiComboBox, - EuiContextMenu, - EuiIcon, EuiComboBoxOptionOption, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { SavedServiceDashboard } from '../../../../common/service_dashboards'; -import { ApmPluginStartDeps } from '../../../plugin'; +enum ContextMenuActionEnum { + Edit = 'edit', + Link = 'link', + Unlink = 'unlink', +} type Props = { serviceDashboards: SavedServiceDashboard[]; selectedDashboard: SavedServiceDashboard; handleOnChange: (selectedId: string) => void; + actions: { id: ContextMenuActionEnum; action: React.ReactNode }[]; + items: React.ReactNode[]; }; export function ContextMenu({ serviceDashboards, selectedDashboard, handleOnChange, + items, }: Props) { - const { - services: { - dashboard: { locator: dashboardLocator }, - }, - } = useKibana(); - const [isPopoverOpen, setPopover] = useState(false); const onButtonClick = () => { @@ -57,63 +54,6 @@ export function ContextMenu({ } }, [selectedDashboard, serviceDashboards]); - const panels = [ - { - id: 0, - title: '', - items: [ - { - name: i18n.translate( - 'xpack.apm.serviceDashboards.contextMenu.linkDashboard', - { - defaultMessage: 'Link new dashboard', - } - ), - icon: 'plusInCircle', - onClick: () => { - closePopover(); - }, - }, - { - name: i18n.translate( - 'xpack.apm.serviceDashboards.contextMenu.goToDashboard', - { - defaultMessage: 'Go to dashboard', - } - ), - icon: 'visGauge', - href: dashboardLocator?.getRedirectUrl({ - dashboardId: selectedDashboard?.dashboardSavedObjectId, - }), - }, - { - name: i18n.translate( - 'xpack.apm.serviceDashboards.contextMenu.visGaugeDashboard', - { - defaultMessage: 'Edit dashboard link', - } - ), - icon: 'pencil', - onClick: () => { - closePopover(); - }, - }, - { - name: i18n.translate( - 'xpack.apm.serviceDashboards.contextMenu.unlinkDashboard', - { - defaultMessage: 'Unlink dashboard', - } - ), - icon: 'unlink', - onClick: () => { - closePopover(); - }, - }, - ], - }, - ]; - return ( <> @@ -167,7 +107,12 @@ export function ContextMenu({ panelPaddingSize="none" anchorPosition="downLeft" > - + ( + {item} + ))} + /> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 3d8ed0c49aff4..de62f047ddb4c 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -5,18 +5,10 @@ * 2.0. */ import React, { useCallback, useEffect, useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { buildPhraseFilter, Filter, TimeRange } from '@kbn/es-query'; -import { - EuiPanel, - EuiFlexGroup, - EuiFlexItem, - EuiComboBoxOptionOption, - EuiComboBox, -} from '@elastic/eui'; +import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EmptyDashboards } from './empty_dashboards'; -import { AddDashboard } from './actions'; +import { GotoDashboard, LinkDashboard } from './actions'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useApmParams } from '../../../hooks/use_apm_params'; import { ViewMode } from '@kbn/embeddable-plugin/public'; @@ -27,6 +19,8 @@ import { } from '@kbn/dashboard-plugin/public'; import { SavedServiceDashboard } from '../../../../common/service_dashboards'; import { ContextMenu } from './context_menu'; +import { UnlinkDashboard } from './actions/unlink_dashboard'; +import { EditDashboard } from './actions/edit_dashboard'; export function ServiceDashboards() { const { @@ -106,6 +100,20 @@ export function ServiceDashboards() { handleOnChange={handleOnChange} selectedDashboard={selectedDashboard} serviceDashboards={data?.serviceDashboards} + items={[ + , + , + , + , + ]} /> @@ -121,7 +129,7 @@ export function ServiceDashboards() { ) : ( => { + const { context, params } = resources; + const { serviceDashboardId } = params.query; + const savedObjectsClient = (await context.core).savedObjects.client; + await deleteServiceDashboard({ + savedObjectsClient, + serviceDashboardId, + }); + }, +}); + export const serviceDashboardsRouteRepository = { ...serviceDashboardSaveRoute, + ...serviceDashboardDeleteRoute, ...serviceDashboardsRoute, }; From bfbdb32ead23438fc64e564e9d942db4a68af8b5 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 19 Sep 2023 23:51:58 +0200 Subject: [PATCH 16/61] Clean up --- .../actions/edit_dashboard.tsx | 3 + .../actions/goto_dashboard.tsx | 8 +- .../actions/select_dashboard_modal.tsx | 5 +- .../actions/unlink_dashboard.tsx | 12 +-- .../app/service_dashboards/context_menu.tsx | 102 +++++------------- .../app/service_dashboards/dropdown.tsx | 62 +++++++++++ .../app/service_dashboards/index.tsx | 73 ++++++++----- 7 files changed, 148 insertions(+), 117 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/service_dashboards/dropdown.tsx diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx index 8be2d1633f529..252ab07236519 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx @@ -7,16 +7,19 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; import { SelectDashboard } from './select_dashboard_modal'; export function EditDashboard({ isModalVisible, setIsModalVisible, onRefresh, + currentDashboard, }: { isModalVisible: boolean; setIsModalVisible: (isVisible: boolean) => void; onRefresh: () => void; + currentDashboard: SavedServiceDashboard; }) { return ( <> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx index 72c0d22c1f9a3..b6fe24a671cc3 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; +import { EuiButtonEmpty } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { i18n } from '@kbn/i18n'; import React from 'react'; @@ -12,9 +12,9 @@ import { ApmPluginStartDeps } from '../../../../plugin'; import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; export function GotoDashboard({ - selectedDashboard, + currentDashboard, }: { - selectedDashboard: SavedServiceDashboard; + currentDashboard: SavedServiceDashboard; }) { const { services: { @@ -23,7 +23,7 @@ export function GotoDashboard({ } = useKibana(); const url = dashboardLocator?.getRedirectUrl({ - dashboardId: selectedDashboard?.dashboardSavedObjectId, + dashboardId: currentDashboard?.dashboardSavedObjectId, }); return ( diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx index 4e1f62982ecc8..67b9c162ca7cc 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx @@ -29,6 +29,7 @@ import { DashboardTypeEnum } from '../../../../../common/service_dashboards'; interface Props { onClose: () => void; + onRefresh: () => void; } export function SelectDashboard({ onClose, onRefresh }: Props) { @@ -46,8 +47,6 @@ export function SelectDashboard({ onClose, onRefresh }: Props) { path: { serviceName }, } = useApmParams('/services/{serviceName}/dashboards'); - console.log('selectedDashboard', selectedDashboard); - const reloadServiceDashboards = useCallback(() => { onRefresh(); }, [onRefresh]); @@ -60,7 +59,7 @@ export function SelectDashboard({ onClose, onRefresh }: Props) { params: { body: { dashboardTitle: newDashboard.label, - dashboardSavedObjectId: newDashboard?.value, + dashboardSavedObjectId: newDashboard.value, useContextFilter, linkTo: DashboardTypeEnum.single, // iteration-1: Only single supported serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx index b461be6d839c3..453580f7d3b3e 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx @@ -12,10 +12,10 @@ import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plug import { callApmApi } from '../../../../services/rest/create_call_apm_api'; export function UnlinkDashboard({ - selectedDashboard, + currentDashboard, onRefresh, }: { - selectedDashboard: SavedServiceDashboard; + currentDashboard?: SavedServiceDashboard; onRefresh: () => void; }) { const [isModalVisible, setIsModalVisible] = useState(false); @@ -27,7 +27,7 @@ export function UnlinkDashboard({ async function () { try { await callApmApi('DELETE /internal/apm/service-dashboard', { - params: { query: { serviceDashboardId: selectedDashboard.id } }, + params: { query: { serviceDashboardId: currentDashboard?.id } }, signal: null, }); @@ -36,7 +36,7 @@ export function UnlinkDashboard({ 'xpack.apm.serviceDashboards.unlinkSuccess.toast.title', { defaultMessage: 'Unlinked "{dashboardName}" dashboard', - values: { dashboardName: selectedDashboard.dashboardTitle }, + values: { dashboardName: currentDashboard?.dashboardTitle }, } ), }); @@ -49,7 +49,7 @@ export function UnlinkDashboard({ { defaultMessage: 'Error while unlinking "{dashboardName}" dashboard', - values: { dashboardName: selectedDashboard.dashboardTitle }, + values: { dashboardName: currentDashboard?.dashboardTitle }, } ), text: error.body.message, @@ -57,7 +57,7 @@ export function UnlinkDashboard({ } setIsModalVisible(!isModalVisible); }, - [selectedDashboard, notifications.toasts] + [currentDashboard, notifications.toasts] ); return ( <> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx index 228d4de1ac04b..1d947a92a68c9 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx @@ -1,33 +1,23 @@ import React, { useEffect, useState } from 'react'; import { EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, EuiContextMenuPanel, EuiContextMenuItem, EuiPopover, - EuiComboBox, EuiComboBoxOptionOption, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import { SavedServiceDashboard } from '../../../../common/service_dashboards'; -enum ContextMenuActionEnum { - Edit = 'edit', - Link = 'link', - Unlink = 'unlink', -} type Props = { serviceDashboards: SavedServiceDashboard[]; - selectedDashboard: SavedServiceDashboard; + currentDashboard: SavedServiceDashboard; handleOnChange: (selectedId: string) => void; - actions: { id: ContextMenuActionEnum; action: React.ReactNode }[]; items: React.ReactNode[]; }; export function ContextMenu({ serviceDashboards, - selectedDashboard, + currentDashboard, handleOnChange, items, }: Props) { @@ -48,74 +38,34 @@ export function ContextMenu({ ]; useEffect(() => { - if (!selectedDashboard && serviceDashboards.length > 0) { + if (!currentDashboard && serviceDashboards.length > 0) { const [serviceDashboard] = serviceDashboards; handleOnChange(serviceDashboard.dashboardSavedObjectId); } - }, [selectedDashboard, serviceDashboards]); + }, [currentDashboard, serviceDashboards]); return ( - <> - - - { - handleOnChange(newItem.value)} - isClearable={false} - /> - } - - - - } - isOpen={isPopoverOpen} - closePopover={closePopover} - panelPaddingSize="none" - anchorPosition="downLeft" - > - ( - {item} - ))} - /> - - - - + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + ( + {item} + ))} + /> + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dropdown.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dropdown.tsx new file mode 100644 index 0000000000000..873eaf7a2f2ca --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dropdown.tsx @@ -0,0 +1,62 @@ +import React, { useEffect } from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { SavedServiceDashboard } from '../../../../common/service_dashboards'; + +type Props = { + serviceDashboards: SavedServiceDashboard[]; + currentDashboard?: SavedServiceDashboard; + handleOnChange: (selectedId: string) => void; +}; + +export function DashboardSelector({ + serviceDashboards, + currentDashboard, + handleOnChange, +}: Props) { + const options: Array> = [ + ...serviceDashboards.map(({ dashboardSavedObjectId, dashboardTitle }) => { + return { label: dashboardTitle, value: dashboardSavedObjectId }; + }), + ]; + + useEffect(() => { + if (!currentDashboard && serviceDashboards.length > 0) { + const [serviceDashboard] = serviceDashboards; + handleOnChange(serviceDashboard.dashboardSavedObjectId); + } + }, [currentDashboard, serviceDashboards]); + + return ( + handleOnChange(newItem.value)} + isClearable={false} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index de62f047ddb4c..fd90d616cebd1 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -21,6 +21,7 @@ import { SavedServiceDashboard } from '../../../../common/service_dashboards'; import { ContextMenu } from './context_menu'; import { UnlinkDashboard } from './actions/unlink_dashboard'; import { EditDashboard } from './actions/edit_dashboard'; +import { DashboardSelector } from './dropdown'; export function ServiceDashboards() { const { @@ -28,7 +29,7 @@ export function ServiceDashboards() { query: { environment, kuery, rangeFrom, rangeTo }, } = useApmParams('/services/{serviceName}/dashboards'); const [dashboard, setDashboard] = useState(); - const [selectedDashboard, setSelectedDashboard] = + const [currentDashboard, setCurrentDashboard] = useState(); const [isModalVisible, setIsModalVisible] = useState(false); @@ -56,7 +57,7 @@ export function ServiceDashboards() { query: { query: kuery, language: 'kuery' }, }); return Promise.resolve({ getInitialInput }); - }, [rangeFrom, rangeTo, kuery, selectedDashboard]); + }, [rangeFrom, rangeTo, kuery, currentDashboard]); const serviceDashboards = data?.serviceDashboards ?? []; @@ -76,11 +77,11 @@ export function ServiceDashboards() { environment, rangeFrom, rangeTo, - selectedDashboard, + currentDashboard, ]); const handleOnChange = (selectedId: string) => { - setSelectedDashboard( + setCurrentDashboard( serviceDashboards.find( ({ dashboardSavedObjectId }) => dashboardSavedObjectId === selectedId ) @@ -93,34 +94,50 @@ export function ServiceDashboards() { <> - {selectedDashboard?.dashboardTitle} - - - , - , - , - , - ]} - /> + {currentDashboard?.dashboardTitle} + + + + + + {currentDashboard && ( + + , + , + , + , + ]} + /> + + )} + - {selectedDashboard && ( + {currentDashboard && ( From 1cf717c9ea2b740384827f4fb673b4935a69808b Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 08:26:46 +0200 Subject: [PATCH 17/61] Move state to the component --- .../actions/edit_dashboard.tsx | 11 ++++---- .../actions/link_dashboard.tsx | 10 ++++---- .../actions/select_dashboard_modal.tsx | 9 ++++++- .../app/service_dashboards/context_menu.tsx | 25 +------------------ .../app/service_dashboards/index.tsx | 21 ++-------------- 5 files changed, 21 insertions(+), 55 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx index 252ab07236519..0dcc940a4b02a 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx @@ -6,21 +6,19 @@ */ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useState } from 'react'; import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; import { SelectDashboard } from './select_dashboard_modal'; export function EditDashboard({ - isModalVisible, - setIsModalVisible, onRefresh, currentDashboard, }: { - isModalVisible: boolean; - setIsModalVisible: (isVisible: boolean) => void; onRefresh: () => void; currentDashboard: SavedServiceDashboard; }) { + const [isModalVisible, setIsModalVisible] = useState(false); + return ( <> setIsModalVisible(true)} > {i18n.translate('xpack.apm.serviceDashboards.editEmptyButtonLabel', { - defaultMessage: 'Edit dashboard', + defaultMessage: 'Edit dashboard link', })} @@ -39,6 +37,7 @@ export function EditDashboard({ setIsModalVisible(false)} onRefresh={onRefresh} + serviceDashboardId={currentDashboard.id} /> )} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 88d211a73ea72..8fa5974100d25 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -6,20 +6,20 @@ */ import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useState } from 'react'; import { SelectDashboard } from './select_dashboard_modal'; export function LinkDashboard({ - isModalVisible, - setIsModalVisible, onRefresh, emptyButton = false, }: { - isModalVisible: boolean; - setIsModalVisible: (isVisible: boolean) => void; + // isModalVisible: boolean; + // setIsModalVisible: (isVisible: boolean) => void; onRefresh: () => void; emptyButton?: Boolean; }) { + const [isModalVisible, setIsModalVisible] = useState(false); + return ( <> {emptyButton ? ( diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx index 67b9c162ca7cc..bc46975c008a3 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx @@ -30,9 +30,14 @@ import { DashboardTypeEnum } from '../../../../../common/service_dashboards'; interface Props { onClose: () => void; onRefresh: () => void; + serviceDashboardId?: string; } -export function SelectDashboard({ onClose, onRefresh }: Props) { +export function SelectDashboard({ + onClose, + onRefresh, + serviceDashboardId, +}: Props) { const { core: { notifications }, } = useApmPluginContext(); @@ -43,6 +48,8 @@ export function SelectDashboard({ onClose, onRefresh }: Props) { Array> >([]); + const isEditMode = !!serviceDashboardId; + const { path: { serviceName }, } = useApmParams('/services/{serviceName}/dashboards'); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx index 1d947a92a68c9..0415de6a4e17f 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx @@ -4,23 +4,13 @@ import { EuiContextMenuPanel, EuiContextMenuItem, EuiPopover, - EuiComboBoxOptionOption, } from '@elastic/eui'; -import { SavedServiceDashboard } from '../../../../common/service_dashboards'; type Props = { - serviceDashboards: SavedServiceDashboard[]; - currentDashboard: SavedServiceDashboard; - handleOnChange: (selectedId: string) => void; items: React.ReactNode[]; }; -export function ContextMenu({ - serviceDashboards, - currentDashboard, - handleOnChange, - items, -}: Props) { +export function ContextMenu({ items }: Props) { const [isPopoverOpen, setPopover] = useState(false); const onButtonClick = () => { @@ -31,19 +21,6 @@ export function ContextMenu({ setPopover(false); }; - const options: Array> = [ - ...serviceDashboards.map(({ dashboardSavedObjectId, dashboardTitle }) => { - return { label: dashboardTitle, value: dashboardSavedObjectId }; - }), - ]; - - useEffect(() => { - if (!currentDashboard && serviceDashboards.length > 0) { - const [serviceDashboard] = serviceDashboards; - handleOnChange(serviceDashboard.dashboardSavedObjectId); - } - }, [currentDashboard, serviceDashboards]); - return ( (); const [currentDashboard, setCurrentDashboard] = useState(); - const [isModalVisible, setIsModalVisible] = useState(false); const { data, status, refetch } = useFetcher( (callApmApi) => { @@ -112,16 +111,8 @@ export function ServiceDashboards() { {currentDashboard && ( , + , , , ) : ( - - } - /> + } /> )} ); From abd138e0628ea669ba90824fe43ebbbb3f305662 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 09:01:21 +0200 Subject: [PATCH 18/61] Add edit mode --- .../actions/edit_dashboard.tsx | 6 +- .../actions/link_dashboard.tsx | 2 - .../actions/select_dashboard_modal.tsx | 68 +++++++++++++------ .../actions/unlink_dashboard.tsx | 6 +- .../app/service_dashboards/index.tsx | 5 +- 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx index 0dcc940a4b02a..e169f6cdf95ec 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx @@ -26,7 +26,7 @@ export function EditDashboard({ size="s" iconType={'pencil'} data-test-subj="apmEditServiceDashboardMenu" - onClick={() => setIsModalVisible(true)} + onClick={() => setIsModalVisible(!isModalVisible)} > {i18n.translate('xpack.apm.serviceDashboards.editEmptyButtonLabel', { defaultMessage: 'Edit dashboard link', @@ -35,9 +35,9 @@ export function EditDashboard({ {isModalVisible && ( setIsModalVisible(false)} + onClose={() => setIsModalVisible(!isModalVisible)} onRefresh={onRefresh} - serviceDashboardId={currentDashboard.id} + currentDashboard={currentDashboard} /> )} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 8fa5974100d25..7647085eaea9e 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -13,8 +13,6 @@ export function LinkDashboard({ onRefresh, emptyButton = false, }: { - // isModalVisible: boolean; - // setIsModalVisible: (isVisible: boolean) => void; onRefresh: () => void; emptyButton?: Boolean; }) { diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx index bc46975c008a3..20770b3718693 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx @@ -26,29 +26,39 @@ import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plug import { useApmParams } from '../../../../hooks/use_apm_params'; import { DashboardItem } from '../../../../../../../../src/plugins/dashboard/common/content_management'; import { DashboardTypeEnum } from '../../../../../common/service_dashboards'; +import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; interface Props { onClose: () => void; onRefresh: () => void; - serviceDashboardId?: string; + currentDashboard?: SavedServiceDashboard; } export function SelectDashboard({ onClose, onRefresh, - serviceDashboardId, + currentDashboard, }: Props) { const { core: { notifications }, } = useApmPluginContext(); - const { data, status } = useDashboardFetcher(); - const [useContextFilter, setUseContextFilter] = useState(true); - const [selectedDashboard, setSelectedDashboard] = useState< - Array> - >([]); + let defaultOption: EuiComboBoxOptionOption | undefined; + + const [useContextFilter, setUseContextFilter] = useState( + currentDashboard?.useContextFilter ?? true + ); + + if (currentDashboard) { + const { dashboardTitle, dashboardSavedObjectId } = currentDashboard; + defaultOption = { label: dashboardTitle, value: dashboardSavedObjectId }; + } + + const [selectedDashboard, setSelectedDashboard] = useState( + defaultOption ? [defaultOption] : [] + ); - const isEditMode = !!serviceDashboardId; + const isEditMode = !!currentDashboard?.id; const { path: { serviceName }, @@ -64,6 +74,7 @@ export function SelectDashboard({ try { await callApmApi('POST /internal/apm/service-dashboard', { params: { + query: { serviceDashboardId: currentDashboard?.id }, body: { dashboardTitle: newDashboard.label, dashboardSavedObjectId: newDashboard.value, @@ -106,14 +117,14 @@ export function SelectDashboard({ } onClose(); }, - [selectedDashboard, notifications.toasts] + [selectedDashboard, notifications.toasts, useContextFilter] ); + console.log('isEditMode', isEditMode); return ( - {' '} {i18n.translate( 'xpack.apm.serviceDashboards.selectDashboard.modalTitle', { @@ -128,12 +139,21 @@ export function SelectDashboard({ ({ label: dashboardItem.attributes.title, @@ -167,9 +187,19 @@ export function SelectDashboard({ )} - {i18n.translate('xpack.apm.serviceDashboards.selectDashboard.add', { - defaultMessage: 'Link dashboard', - })} + {isEditMode + ? i18n.translate( + 'xpack.apm.serviceDashboards.selectDashboard.edit', + { + defaultMessage: 'Save', + } + ) + : i18n.translate( + 'xpack.apm.serviceDashboards.selectDashboard.add', + { + defaultMessage: 'Link dashboard', + } + )} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx index 453580f7d3b3e..6b3a78a645d64 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx @@ -15,7 +15,7 @@ export function UnlinkDashboard({ currentDashboard, onRefresh, }: { - currentDashboard?: SavedServiceDashboard; + currentDashboard: SavedServiceDashboard; onRefresh: () => void; }) { const [isModalVisible, setIsModalVisible] = useState(false); @@ -27,7 +27,7 @@ export function UnlinkDashboard({ async function () { try { await callApmApi('DELETE /internal/apm/service-dashboard', { - params: { query: { serviceDashboardId: currentDashboard?.id } }, + params: { query: { serviceDashboardId: currentDashboard.id } }, signal: null, }); @@ -36,7 +36,7 @@ export function UnlinkDashboard({ 'xpack.apm.serviceDashboards.unlinkSuccess.toast.title', { defaultMessage: 'Unlinked "{dashboardName}" dashboard', - values: { dashboardName: currentDashboard?.dashboardTitle }, + values: { dashboardName: currentDashboard.dashboardTitle }, } ), }); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 26662c1d0af70..1eb7e06469739 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -114,7 +114,10 @@ export function ServiceDashboards() { items={[ , , - , + , Date: Wed, 20 Sep 2023 07:37:32 +0000 Subject: [PATCH 19/61] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../service_dashboards/actions/goto_dashboard.tsx | 8 +++++++- .../service_dashboards/actions/link_dashboard.tsx | 2 +- .../actions/select_dashboard_modal.tsx | 14 +++++++++++--- .../app/service_dashboards/context_menu.tsx | 14 +++++++++++--- .../components/app/service_dashboards/dropdown.tsx | 11 +++++++++-- .../components/app/service_dashboards/index.tsx | 8 ++++---- .../apm/public/hooks/use_dashboards_fetcher.ts | 2 +- 7 files changed, 44 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx index b6fe24a671cc3..7a5cb49aaae6c 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx @@ -26,7 +26,13 @@ export function GotoDashboard({ dashboardId: currentDashboard?.dashboardSavedObjectId, }); return ( - + {i18n.translate('xpack.apm.serviceDashboards.contextMenu.goToDashboard', { defaultMessage: 'Go to dashboard', })} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 7647085eaea9e..de84bd41d6a5b 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -14,7 +14,7 @@ export function LinkDashboard({ emptyButton = false, }: { onRefresh: () => void; - emptyButton?: Boolean; + emptyButton?: boolean; }) { const [isModalVisible, setIsModalVisible] = useState(false); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx index 20770b3718693..edd140c738f7c 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx @@ -19,12 +19,12 @@ import { EuiFlexGroup, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { DashboardItem } from '@kbn/dashboard-plugin/common/content_management'; import { callApmApi } from '../../../../services/rest/create_call_apm_api'; import { useDashboardFetcher } from '../../../../hooks/use_dashboards_fetcher'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useApmParams } from '../../../../hooks/use_apm_params'; -import { DashboardItem } from '../../../../../../../../src/plugins/dashboard/common/content_management'; import { DashboardTypeEnum } from '../../../../../common/service_dashboards'; import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; @@ -178,7 +178,11 @@ export function SelectDashboard({
- + {i18n.translate( 'xpack.apm.serviceDashboards.selectDashboard.cancel', { @@ -186,7 +190,11 @@ export function SelectDashboard({ } )} - + {isEditMode ? i18n.translate( 'xpack.apm.serviceDashboards.selectDashboard.edit', diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx index 0415de6a4e17f..2eb48b7f66848 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/context_menu.tsx @@ -1,4 +1,11 @@ -import React, { useEffect, useState } from 'react'; +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { EuiButtonIcon, EuiContextMenuPanel, @@ -6,9 +13,9 @@ import { EuiPopover, } from '@elastic/eui'; -type Props = { +interface Props { items: React.ReactNode[]; -}; +} export function ContextMenu({ items }: Props) { const [isPopoverOpen, setPopover] = useState(false); @@ -25,6 +32,7 @@ export function ContextMenu({ items }: Props) { void; -}; +} export function DashboardSelector({ serviceDashboards, diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 1eb7e06469739..b41e84b06986f 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -7,16 +7,16 @@ import React, { useCallback, useEffect, useState } from 'react'; import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { EmptyDashboards } from './empty_dashboards'; -import { GotoDashboard, LinkDashboard } from './actions'; -import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; -import { useApmParams } from '../../../hooks/use_apm_params'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { AwaitingDashboardAPI, DashboardCreationOptions, DashboardRenderer, } from '@kbn/dashboard-plugin/public'; +import { EmptyDashboards } from './empty_dashboards'; +import { GotoDashboard, LinkDashboard } from './actions'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; +import { useApmParams } from '../../../hooks/use_apm_params'; import { SavedServiceDashboard } from '../../../../common/service_dashboards'; import { ContextMenu } from './context_menu'; import { UnlinkDashboard } from './actions/unlink_dashboard'; diff --git a/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts index 58bed3dbe3bcd..731628fa6ce53 100644 --- a/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts @@ -7,9 +7,9 @@ import { useState, useEffect } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { SearchDashboardsResponse } from '@kbn/dashboard-plugin/public/services/dashboard_content_management/lib/find_dashboards'; import { ApmPluginStartDeps } from '../plugin'; import { FETCH_STATUS } from './use_fetcher'; -import { SearchDashboardsResponse } from '../../../../../src/plugins/dashboard/public/services/dashboard_content_management/lib/find_dashboards'; export interface SearchDashboardsResult { data: SearchDashboardsResponse['hits']; From ed23453c1382a2ef5b4466e117ecf17776cbfc12 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 10:52:53 +0200 Subject: [PATCH 20/61] Rename components --- .../actions/edit_dashboard.tsx | 5 +- .../actions/link_dashboard.tsx | 4 +- ...ard_modal.tsx => save_dashboard_modal.tsx} | 96 +++++++++++-------- .../{dropdown.tsx => dashboard_selector.tsx} | 2 +- .../app/service_dashboards/index.tsx | 2 +- 5 files changed, 63 insertions(+), 46 deletions(-) rename x-pack/plugins/apm/public/components/app/service_dashboards/actions/{select_dashboard_modal.tsx => save_dashboard_modal.tsx} (74%) rename x-pack/plugins/apm/public/components/app/service_dashboards/{dropdown.tsx => dashboard_selector.tsx} (97%) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx index e169f6cdf95ec..1aecc7ea814ff 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx @@ -8,7 +8,7 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; -import { SelectDashboard } from './select_dashboard_modal'; +import { SaveDashboardModal } from './save_dashboard_modal'; export function EditDashboard({ onRefresh, @@ -18,7 +18,6 @@ export function EditDashboard({ currentDashboard: SavedServiceDashboard; }) { const [isModalVisible, setIsModalVisible] = useState(false); - return ( <> {isModalVisible && ( - setIsModalVisible(!isModalVisible)} onRefresh={onRefresh} currentDashboard={currentDashboard} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 7647085eaea9e..9a987ff8225cd 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -7,7 +7,7 @@ import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { SelectDashboard } from './select_dashboard_modal'; +import { SaveDashboardModal } from './save_dashboard_modal'; export function LinkDashboard({ onRefresh, @@ -44,7 +44,7 @@ export function LinkDashboard({ )} {isModalVisible && ( - setIsModalVisible(false)} onRefresh={onRefresh} /> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx similarity index 74% rename from x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx rename to x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 20770b3718693..eacfc5087d305 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/select_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -34,7 +34,7 @@ interface Props { currentDashboard?: SavedServiceDashboard; } -export function SelectDashboard({ +export function SaveDashboardModal({ onClose, onRefresh, currentDashboard, @@ -85,22 +85,12 @@ export function SelectDashboard({ }, signal: null, }); - notifications.toasts.addSuccess({ - title: i18n.translate( - 'xpack.apm.serviceDashboards.addSuccess.toast.title', - { - defaultMessage: 'Added "{dashboardName}" dashboard', - values: { dashboardName: newDashboard.label }, - } - ), - text: i18n.translate( - 'xpack.apm.serviceDashboards.addSuccess.toast.text', - { - defaultMessage: - 'Your dashboard is now visible in the service overview page.', - } - ), - }); + + notifications.toasts.addSuccess( + isEditMode + ? getEditSuccessToastLabels(newDashboard.label) + : getLinkSuccessToastLabels(newDashboard.label) + ); reloadServiceDashboards(); } catch (error) { console.error(error); @@ -120,17 +110,23 @@ export function SelectDashboard({ [selectedDashboard, notifications.toasts, useContextFilter] ); - console.log('isEditMode', isEditMode); return ( - {i18n.translate( - 'xpack.apm.serviceDashboards.selectDashboard.modalTitle', - { - defaultMessage: 'Select dashboard', - } - )} + {isEditMode + ? i18n.translate( + 'xpack.apm.serviceDashboards.selectDashboard.modalTitle.edit', + { + defaultMessage: 'Edit dashboard', + } + ) + : i18n.translate( + 'xpack.apm.serviceDashboards.selectDashboard.modalTitle.link', + { + defaultMessage: 'Select dashboard', + } + )} @@ -139,21 +135,12 @@ export function SelectDashboard({ ({ label: dashboardItem.attributes.title, @@ -205,3 +192,34 @@ export function SelectDashboard({ ); } + +function getLinkSuccessToastLabels(dashboardName: string) { + return { + title: i18n.translate( + 'xpack.apm.serviceDashboards.linkSuccess.toast.title', + { + defaultMessage: 'Added "{dashboardName}" dashboard', + values: { dashboardName }, + } + ), + text: i18n.translate('xpack.apm.serviceDashboards.linkSuccess.toast.text', { + defaultMessage: + 'Your dashboard is now visible in the service overview page.', + }), + }; +} + +function getEditSuccessToastLabels(dashboardName: string) { + return { + title: i18n.translate( + 'xpack.apm.serviceDashboards.linkSuccess.toast.title', + { + defaultMessage: 'Edited "{dashboardName}" dashboard', + values: { dashboardName }, + } + ), + text: i18n.translate('xpack.apm.serviceDashboards.linkSuccess.toast.text', { + defaultMessage: 'Your dashboard link have been updated', + }), + }; +} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dropdown.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx similarity index 97% rename from x-pack/plugins/apm/public/components/app/service_dashboards/dropdown.tsx rename to x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 873eaf7a2f2ca..9474980652657 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dropdown.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { SavedServiceDashboard } from '../../../../common/service_dashboards'; diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 1eb7e06469739..97d93fbb34092 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -21,7 +21,7 @@ import { SavedServiceDashboard } from '../../../../common/service_dashboards'; import { ContextMenu } from './context_menu'; import { UnlinkDashboard } from './actions/unlink_dashboard'; import { EditDashboard } from './actions/edit_dashboard'; -import { DashboardSelector } from './dropdown'; +import { DashboardSelector } from './dashboard_selector'; export function ServiceDashboards() { const { From 8124da37b77a9d87bada008973a412fe471659e0 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 11:07:42 +0200 Subject: [PATCH 21/61] Polish the UI --- .../service_dashboards/empty_dashboards.tsx | 14 ++- .../app/service_dashboards/index.tsx | 85 ++++++++++--------- 2 files changed, 56 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx index c2fc38a1f3882..c80de97d58812 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx @@ -10,6 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiImage, + EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg'; @@ -26,9 +27,16 @@ export function EmptyDashboards({ actions }: Props) { <> - {i18n.translate('xpack.apm.serviceDashboards.selectDashboard.title', { - defaultMessage: 'Custom', - })} + +

+ {i18n.translate( + 'xpack.apm.serviceDashboards.selectDashboard.title', + { + defaultMessage: 'Custom', + } + )} +

+
{status !== FETCH_STATUS.LOADING && serviceDashboards.length > 0 ? ( <> - + + + +

{currentDashboard?.dashboardTitle}

+
+
+ - {currentDashboard?.dashboardTitle} + - + + {currentDashboard && ( - , + , + , + , + ]} /> - - {currentDashboard && ( - - , - , - , - , - ]} - /> - - )} - + )}
- - {currentDashboard && ( - - )} + + + {currentDashboard && ( + + )} + ) : ( } /> From b5576e29735984bd01a8f7c4a9570a9d08e2f3e2 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 18:08:35 +0200 Subject: [PATCH 22/61] Fix loading --- .../actions/save_dashboard_modal.tsx | 2 +- .../app/service_dashboards/index.tsx | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index eacfc5087d305..8a1293a9df044 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -134,7 +134,7 @@ export function SaveDashboardModal({ - {status !== FETCH_STATUS.LOADING && serviceDashboards.length > 0 ? ( + {status === FETCH_STATUS.LOADING ? ( + } + title={ +

+ {i18n.translate( + 'xpack.apm.serviceDashboards.loadingServiceDashboards', + { + defaultMessage: 'Loading service dashboard', + } + )} +

+ } + /> + ) : status === FETCH_STATUS.SUCCESS && serviceDashboards.length > 0 ? ( <> From c00caa18cf96a0c076e02638343cfbdf65d098d7 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 18:11:01 +0200 Subject: [PATCH 23/61] Disable adding existing service dashboards --- .../actions/link_dashboard.tsx | 4 ++++ .../actions/save_dashboard_modal.tsx | 20 ++++++++++++++----- .../app/service_dashboards/index.tsx | 9 ++++++--- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 9a987ff8225cd..49c06833afaaf 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -7,14 +7,17 @@ import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; +import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; import { SaveDashboardModal } from './save_dashboard_modal'; export function LinkDashboard({ onRefresh, emptyButton = false, + serviceDashboards, }: { onRefresh: () => void; emptyButton?: Boolean; + serviceDashboards?: SavedServiceDashboard; }) { const [isModalVisible, setIsModalVisible] = useState(false); @@ -47,6 +50,7 @@ export function LinkDashboard({ setIsModalVisible(false)} onRefresh={onRefresh} + serviceDashboards={serviceDashboards} /> )} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 8a1293a9df044..6fa8391d1f7ea 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -32,17 +32,19 @@ interface Props { onClose: () => void; onRefresh: () => void; currentDashboard?: SavedServiceDashboard; + serviceDashboards?: SavedServiceDashboard[]; } export function SaveDashboardModal({ onClose, onRefresh, currentDashboard, + serviceDashboards, }: Props) { const { core: { notifications }, } = useApmPluginContext(); - const { data, status } = useDashboardFetcher(); + const { data: allAvailableDashboards, status } = useDashboardFetcher(); let defaultOption: EuiComboBoxOptionOption | undefined; const [useContextFilter, setUseContextFilter] = useState( @@ -68,6 +70,17 @@ export function SaveDashboardModal({ onRefresh(); }, [onRefresh]); + const options = allAvailableDashboards?.map( + (dashboardItem: DashboardItem) => ({ + label: dashboardItem.attributes.title, + value: dashboardItem.id, + disabled: + serviceDashboards?.some( + ({ dashboardSavedObjectId }) => + dashboardItem.id === dashboardSavedObjectId + ) ?? false, + }) + ); const onSave = useCallback( async function () { const [newDashboard] = selectedDashboard; @@ -142,10 +155,7 @@ export function SaveDashboardModal({ } )} singleSelection={{ asPlainText: true }} - options={data?.map((dashboardItem: DashboardItem) => ({ - label: dashboardItem.attributes.title, - value: dashboardItem.id, - }))} + options={options} selectedOptions={selectedDashboard} onChange={(newSelection) => setSelectedDashboard(newSelection)} isClearable={true} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index e9ada8ed1cdb3..79139f46629a5 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -56,6 +56,7 @@ export function ServiceDashboards() { }, [serviceName] ); + const serviceDashboards = data?.serviceDashboards ?? []; const getCreationOptions = useCallback((): Promise => { @@ -67,8 +68,6 @@ export function ServiceDashboards() { return Promise.resolve({ getInitialInput }); }, [rangeFrom, rangeTo, kuery, currentDashboard]); - const serviceDashboards = data?.serviceDashboards ?? []; - useEffect(() => { if (!dashboard) return; @@ -133,7 +132,11 @@ export function ServiceDashboards() { , + , , Date: Wed, 20 Sep 2023 21:38:17 +0200 Subject: [PATCH 24/61] Fix preselect current dashboard --- .../service_dashboards/dashboard_selector.tsx | 19 +++++-------------- .../service_dashboards/empty_dashboards.tsx | 14 -------------- .../app/service_dashboards/index.tsx | 14 ++++++++++---- 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 9474980652657..a4a772252b99c 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -14,19 +14,6 @@ export function DashboardSelector({ currentDashboard, handleOnChange, }: Props) { - const options: Array> = [ - ...serviceDashboards.map(({ dashboardSavedObjectId, dashboardTitle }) => { - return { label: dashboardTitle, value: dashboardSavedObjectId }; - }), - ]; - - useEffect(() => { - if (!currentDashboard && serviceDashboards.length > 0) { - const [serviceDashboard] = serviceDashboards; - handleOnChange(serviceDashboard.dashboardSavedObjectId); - } - }, [currentDashboard, serviceDashboards]); - return ( { + return { label: dashboardTitle, value: dashboardSavedObjectId }; + } + )} selectedOptions={ currentDashboard ? [ diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx index c80de97d58812..6c1d8ffce4406 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx @@ -25,20 +25,6 @@ export function EmptyDashboards({ actions }: Props) { return ( <> - - - -

- {i18n.translate( - 'xpack.apm.serviceDashboards.selectDashboard.title', - { - defaultMessage: 'Custom', - } - )} -

-
-
-
{ + // preselect dashboard + setCurrentDashboard(data?.serviceDashboards[0]); + }, [serviceDashboards]); + const getCreationOptions = useCallback((): Promise => { const getInitialInput = () => ({ @@ -101,22 +107,22 @@ export function ServiceDashboards() { } title={ -

+

{i18n.translate( 'xpack.apm.serviceDashboards.loadingServiceDashboards', { defaultMessage: 'Loading service dashboard', } )} -

+ } /> ) : status === FETCH_STATUS.SUCCESS && serviceDashboards.length > 0 ? ( <> - -

{currentDashboard?.dashboardTitle}

+ +

{currentDashboard?.dashboardTitle}

From ed642a3533d8a083ad6c19820cbd7a195fe14e0c Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 21:50:01 +0200 Subject: [PATCH 25/61] Return latest updated service dashboard --- .../server/routes/service_dashboards/get_service_dashboards.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts index 136109a17e70a..b488e0a96eca6 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts @@ -26,6 +26,8 @@ export async function getServiceDashboards({ page: 1, perPage: 100, filter, + sortField: 'updated_at', + sortOrder: 'desc', }); return result.saved_objects.map( From 26f526a7c4dc5f8e5d2ac80fc93a7bc0fd6a476c Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 20 Sep 2023 23:00:57 +0200 Subject: [PATCH 26/61] Use context filter --- .../app/metrics/static_dashboard/index.tsx | 4 +-- .../actions/save_dashboard_modal.tsx | 20 ++++++++----- .../app/service_dashboards/index.tsx | 29 ++++++++++--------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/metrics/static_dashboard/index.tsx b/x-pack/plugins/apm/public/components/app/metrics/static_dashboard/index.tsx index 789239ffa1d68..50bda742eb377 100644 --- a/x-pack/plugins/apm/public/components/app/metrics/static_dashboard/index.tsx +++ b/x-pack/plugins/apm/public/components/app/metrics/static_dashboard/index.tsx @@ -109,7 +109,7 @@ async function getCreationOptions( } } -function getFilters( +export function getFilters( serviceName: string, environment: string, dataView: DataView @@ -139,7 +139,7 @@ function getFilters( } else { const environmentFilter = buildPhraseFilter( environmentField, - serviceName, + environment, dataView ); filters.push(environmentFilter); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 6fa8391d1f7ea..e009c1e0f554a 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -17,6 +17,7 @@ import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexGroup, + EuiMarkdownFormat, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { callApmApi } from '../../../../services/rest/create_call_apm_api'; @@ -151,7 +152,7 @@ export function SaveDashboardModal({ placeholder={i18n.translate( 'xpack.apm.serviceDashboards.selectDashboard.placeholder', { - defaultMessage: 'Select dasbboard test', + defaultMessage: 'Select dasbboard', } )} singleSelection={{ asPlainText: true }} @@ -162,12 +163,17 @@ export function SaveDashboardModal({ /> + {i18n.translate( + 'xpack.apm.dashboard.addDashboard.useContextFilterLabel', + { + defaultMessage: 'Filter by `service` and `environment`', + } + )} + + } onChange={() => setUseContextFilter(!useContextFilter)} checked={useContextFilter} /> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index b6ed3f0fbf610..0cfccbbacfaae 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -31,6 +31,8 @@ import { ContextMenu } from './context_menu'; import { UnlinkDashboard } from './actions/unlink_dashboard'; import { EditDashboard } from './actions/edit_dashboard'; import { DashboardSelector } from './dashboard_selector'; +import { useApmDataView } from '../../../hooks/use_apm_data_view'; +import { getFilters } from '../metrics/static_dashboard'; export function ServiceDashboards() { const { @@ -40,6 +42,7 @@ export function ServiceDashboards() { const [dashboard, setDashboard] = useState(); const [currentDashboard, setCurrentDashboard] = useState(); + const { dataView } = useApmDataView(); const { data, status, refetch } = useFetcher( (callApmApi) => { @@ -69,29 +72,29 @@ export function ServiceDashboards() { const getInitialInput = () => ({ viewMode: ViewMode.VIEW, timeRange: { from: rangeFrom, to: rangeTo }, - query: { query: kuery, language: 'kuery' }, }); return Promise.resolve({ getInitialInput }); - }, [rangeFrom, rangeTo, kuery, currentDashboard]); + }, []); useEffect(() => { if (!dashboard) return; dashboard.updateInput({ - viewMode: ViewMode.VIEW, timeRange: { from: rangeFrom, to: rangeTo }, - // TODO useContextFilter query: { query: kuery, language: 'kuery' }, }); - }, [ - serviceDashboards, - kuery, - serviceName, - environment, - rangeFrom, - rangeTo, - currentDashboard, - ]); + }, [kuery, dashboard, rangeFrom, rangeTo]); + + useEffect(() => { + if (!dashboard || !dataView) return; + + dashboard.updateInput({ + filters: + dataView && currentDashboard?.useContextFilter + ? getFilters(serviceName, environment, dataView) + : [], + }); + }, [dataView, serviceName, environment, dashboard, currentDashboard]); const handleOnChange = (selectedId: string) => { setCurrentDashboard( From b825d5786fb384901437835d45b98e467510c2e4 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 20 Sep 2023 21:47:45 +0000 Subject: [PATCH 27/61] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../app/service_dashboards/actions/link_dashboard.tsx | 2 +- .../app/service_dashboards/empty_dashboards.tsx | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 49c06833afaaf..b25b9ae384258 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -16,7 +16,7 @@ export function LinkDashboard({ serviceDashboards, }: { onRefresh: () => void; - emptyButton?: Boolean; + emptyButton?: boolean; serviceDashboards?: SavedServiceDashboard; }) { const [isModalVisible, setIsModalVisible] = useState(false); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx index 6c1d8ffce4406..423075fdb9ffb 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx @@ -5,13 +5,7 @@ * 2.0. */ import React from 'react'; -import { - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiImage, - EuiTitle, -} from '@elastic/eui'; +import { EuiEmptyPrompt, EuiImage } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg'; import { useTheme } from '../../../hooks/use_theme'; From 61b98e0f04d04bf5bbb6fb9d4d9299f2c68ed5d9 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Thu, 21 Sep 2023 07:37:57 +0200 Subject: [PATCH 28/61] Fix packages --- x-pack/plugins/apm/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index c53c22434f55b..2c225c509fad6 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -102,7 +102,7 @@ "@kbn/core-analytics-server", "@kbn/analytics-client", "@kbn/monaco", - "@kbn/shared-svg" + "@kbn/shared-svg", "@kbn/deeplinks-observability" ], "exclude": ["target/**/*"] From e41add7f2f2419d4de869868c8267787e56c9114 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Thu, 21 Sep 2023 07:49:02 +0200 Subject: [PATCH 29/61] Fix types --- .../actions/link_dashboard.tsx | 2 +- .../actions/save_dashboard_modal.tsx | 41 ++++++++++--------- .../service_dashboards/dashboard_selector.tsx | 2 +- .../app/service_dashboards/index.tsx | 2 +- .../server/routes/service_dashboards/route.ts | 2 +- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 49c06833afaaf..39a6ec6ba3a20 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -17,7 +17,7 @@ export function LinkDashboard({ }: { onRefresh: () => void; emptyButton?: Boolean; - serviceDashboards?: SavedServiceDashboard; + serviceDashboards?: SavedServiceDashboard[]; }) { const [isModalVisible, setIsModalVisible] = useState(false); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 1abca954f45ad..6ae1e52adeb46 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -86,26 +86,29 @@ export function SaveDashboardModal({ async function () { const [newDashboard] = selectedDashboard; try { - await callApmApi('POST /internal/apm/service-dashboard', { - params: { - query: { serviceDashboardId: currentDashboard?.id }, - body: { - dashboardTitle: newDashboard.label, - dashboardSavedObjectId: newDashboard.value, - useContextFilter, - linkTo: DashboardTypeEnum.single, // iteration-1: Only single supported - serviceName, + if (newDashboard.value) { + await callApmApi('POST /internal/apm/service-dashboard', { + params: { + query: { serviceDashboardId: currentDashboard?.id }, + body: { + dashboardTitle: newDashboard.label, + dashboardSavedObjectId: newDashboard.value, + useContextFilter, + linkTo: DashboardTypeEnum.single, // iteration-1: Only single supported + serviceName, + kuery: undefined, + }, }, - }, - signal: null, - }); - - notifications.toasts.addSuccess( - isEditMode - ? getEditSuccessToastLabels(newDashboard.label) - : getLinkSuccessToastLabels(newDashboard.label) - ); - reloadServiceDashboards(); + signal: null, + }); + + notifications.toasts.addSuccess( + isEditMode + ? getEditSuccessToastLabels(newDashboard.label) + : getLinkSuccessToastLabels(newDashboard.label) + ); + reloadServiceDashboards(); + } } catch (error) { console.error(error); notifications.toasts.addDanger({ diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 628fe3e0a5789..7165d1ac1adeb 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -13,7 +13,7 @@ import { SavedServiceDashboard } from '../../../../common/service_dashboards'; interface Props { serviceDashboards: SavedServiceDashboard[]; currentDashboard?: SavedServiceDashboard; - handleOnChange: (selectedId: string) => void; + handleOnChange: (selectedId?: string) => void; } export function DashboardSelector({ diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 0fed37449323a..4821f6b86b540 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -96,7 +96,7 @@ export function ServiceDashboards() { }); }, [dataView, serviceName, environment, dashboard, currentDashboard]); - const handleOnChange = (selectedId: string) => { + const handleOnChange = (selectedId?: string) => { setCurrentDashboard( serviceDashboards.find( ({ dashboardSavedObjectId }) => dashboardSavedObjectId === selectedId diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index 26dcea697b0af..4462b68902d24 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -34,7 +34,7 @@ const serviceDashboardSaveRoute = createApmServerRoute({ dashboardSavedObjectId: t.string, dashboardTitle: t.string, kuery: t.union([t.string, t.undefined]), - serviceName: t.union([t.string, t.undefined]), + serviceName: t.string, useContextFilter: t.boolean, linkTo: linkToRt, }), From 49380157afbffbc377dbdaa105a3667f7d37106a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 21 Sep 2023 06:22:07 +0000 Subject: [PATCH 30/61] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../app/service_dashboards/actions/link_dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 39a6ec6ba3a20..73c55c0f8b2ee 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -16,7 +16,7 @@ export function LinkDashboard({ serviceDashboards, }: { onRefresh: () => void; - emptyButton?: Boolean; + emptyButton?: boolean; serviceDashboards?: SavedServiceDashboard[]; }) { const [isModalVisible, setIsModalVisible] = useState(false); From 3f996b7fe55ea528e5e29e1e6bafd653b6184fac Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Thu, 21 Sep 2023 09:32:53 +0200 Subject: [PATCH 31/61] Revert change regarding telemetry task --- .../collect_data_telemetry/tasks.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index adbd6be389743..aad51e5ed1c5d 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -559,7 +559,24 @@ export const tasks: TelemetryTask[] = [ }, { name: 'agent_configuration', - executor: async ({ indices, telemetryClient }) => {}, + executor: async ({ indices, telemetryClient }) => { + const agentConfigurationCount = await telemetryClient.search({ + index: APM_AGENT_CONFIGURATION_INDEX, + body: { + size: 0, + timeout, + track_total_hits: true, + }, + }); + + return { + counts: { + agent_configuration: { + all: agentConfigurationCount.hits.total.value, + }, + }, + }; + }, }, { name: 'services', From 7fc3ddc830781ed6999cb9c2a259e99da507c610 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Thu, 21 Sep 2023 10:29:18 +0200 Subject: [PATCH 32/61] Address design feedback --- .../actions/save_dashboard_modal.tsx | 24 +++++++++++++++---- .../app/service_dashboards/index.tsx | 6 ++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 6ae1e52adeb46..b09601e66c614 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -17,7 +17,8 @@ import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexGroup, - EuiMarkdownFormat, + EuiToolTip, + EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DashboardItem } from '@kbn/dashboard-plugin/common/content_management'; @@ -166,16 +167,29 @@ export function SaveDashboardModal({ /> +

{i18n.translate( 'xpack.apm.dashboard.addDashboard.useContextFilterLabel', { - defaultMessage: 'Filter by `service` and `environment`', + defaultMessage: 'Filter by service and environment', } - )} - + )}{' '} + + + +

} onChange={() => setUseContextFilter(!useContextFilter)} checked={useContextFilter} diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 4821f6b86b540..b2a4861e45562 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -122,7 +122,11 @@ export function ServiceDashboards() { /> ) : status === FETCH_STATUS.SUCCESS && serviceDashboards.length > 0 ? ( <> - +

{currentDashboard?.dashboardTitle}

From d3d9fda4d180ddc47751e0cccaba7b6aaa4e5bf7 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Thu, 21 Sep 2023 11:40:19 +0200 Subject: [PATCH 33/61] Add api tests --- .../tests/service_dashboards/api_helper.ts | 70 ++++++++++++++ .../service_dashboards.spec.ts | 96 +++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts create mode 100644 x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts new file mode 100644 index 0000000000000..5d8d95376ea25 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DashboardTypeEnum } from '../../../../plugins/apm/common/service_dashboards'; +import { ApmApiClient } from '../../common/config'; + +export async function getServiceDashboardApi(apmApiClient: ApmApiClient, serviceName: string) { + return apmApiClient.writeUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/dashboards', + params: { + path: { serviceName }, + }, + }); +} + +export async function getLinkServiceDashboardApi({ + dashboardSavedObjectId, + dashboardTitle, + apmApiClient, + serviceDashboardId, + serviceName, + kuery, + linkTo, + useContextFilter, +}: { + apmApiClient: ApmApiClient; + dashboardSavedObjectId: string; + dashboardTitle: string; + serviceDashboardId?: string; + serviceName: string; + kuery: string; + linkTo: DashboardTypeEnum; + useContextFilter: boolean; +}) { + const response = await apmApiClient.writeUser({ + endpoint: 'POST /internal/apm/service-dashboard', + params: { + query: { + serviceDashboardId, + }, + body: { + dashboardSavedObjectId, + dashboardTitle, + kuery, + serviceName, + useContextFilter, + linkTo, + }, + }, + }); + return response; +} + +export async function deleteAllServiceDashboard(apmApiClient: ApmApiClient, serviceName: string) { + return await getServiceDashboardApi(apmApiClient, serviceName).then((response) => { + const promises = response.body.serviceDashboards.map((item) => { + if (item.id) { + return apmApiClient.writeUser({ + endpoint: 'DELETE /internal/apm/service-dashboard', + params: { query: { serviceDashboardId: item.id } }, + }); + } + }); + return Promise.all(promises); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts new file mode 100644 index 0000000000000..91b6a6d33bee3 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { DashboardTypeEnum } from '../../../../plugins/apm/common/service_dashboards'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + getServiceDashboardApi, + getLinkServiceDashboardApi, + deleteAllServiceDashboard, +} from './api_helper'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); + const apmApiClient = getService('apmApiClient'); + + registry.when('Service dashboard link', { config: 'basic', archives: [] }, () => { + afterEach(async () => { + await deleteAllServiceDashboard(apmApiClient, 'synth'); + }); + + it('creates a new service dashboard', async () => { + const serviceDashboard = { + serviceName: 'synth', + dashboardTitle: 'my dashboard', + dashboardSavedObjectId: 'dashboard-saved-object-id', + linkTo: DashboardTypeEnum.single, + useContextFilter: true, + kuery: '', + }; + const createResponse = await getLinkServiceDashboardApi({ + apmApiClient, + ...serviceDashboard, + }); + expect(createResponse.status).to.be(200); + expect(createResponse.body).to.have.property('id'); + expect(createResponse.body).to.have.property('updatedAt'); + + expect(createResponse.body).to.have.property('serviceName', serviceDashboard.serviceName); + expect(createResponse.body).to.have.property( + 'dashboardTitle', + serviceDashboard.dashboardTitle + ); + + expect(createResponse.body).to.have.property( + 'dashboardSavedObjectId', + serviceDashboard.dashboardSavedObjectId + ); + expect(createResponse.body).to.have.property( + 'useContextFilter', + serviceDashboard.useContextFilter + ); + expect(createResponse.body).to.have.property('linkTo', serviceDashboard.linkTo); + + const serviceDashboardResponse = await getServiceDashboardApi(apmApiClient, 'synth'); + expect(serviceDashboardResponse.body.serviceDashboards.length).to.be(1); + }); + + it('updates the existing linked service dashboard', async () => { + const serviceDashboard = { + serviceName: 'synth', + dashboardTitle: 'my dashboard', + dashboardSavedObjectId: 'dashboard-saved-object-id', + linkTo: DashboardTypeEnum.single, + useContextFilter: true, + kuery: '', + }; + + await getLinkServiceDashboardApi({ + apmApiClient, + ...serviceDashboard, + }); + + const serviceDashboardResponse = await getServiceDashboardApi(apmApiClient, 'synth'); + + const updateResponse = await getLinkServiceDashboardApi({ + apmApiClient, + serviceDashboardId: serviceDashboardResponse.body.serviceDashboards[0].id, + ...serviceDashboard, + useContextFilter: true, + }); + + expect(updateResponse.status).to.be(200); + + const updatedServiceDashboardResponse = await getServiceDashboardApi(apmApiClient, 'synth'); + expect(updatedServiceDashboardResponse.body.serviceDashboards.length).to.be(1); + expect(updatedServiceDashboardResponse.body.serviceDashboards[0]).to.have.property( + 'useContextFilter', + true + ); + }); + }); +} From 6cdf44a554a87a92e7d4bb1fb73bc572db3b378c Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 21 Sep 2023 09:47:01 +0000 Subject: [PATCH 34/61] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../apm_api_integration/tests/service_dashboards/api_helper.ts | 2 +- .../tests/service_dashboards/service_dashboards.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts index 5d8d95376ea25..b77ffb77dc245 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DashboardTypeEnum } from '../../../../plugins/apm/common/service_dashboards'; +import { DashboardTypeEnum } from '@kbn/apm-plugin/common/service_dashboards'; import { ApmApiClient } from '../../common/config'; export async function getServiceDashboardApi(apmApiClient: ApmApiClient, serviceName: string) { diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts index 91b6a6d33bee3..00c1f2aba15fc 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts @@ -5,7 +5,7 @@ * 2.0. */ import expect from '@kbn/expect'; -import { DashboardTypeEnum } from '../../../../plugins/apm/common/service_dashboards'; +import { DashboardTypeEnum } from '@kbn/apm-plugin/common/service_dashboards'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { getServiceDashboardApi, From 511ff5445e459675077cc791066480a41d1cdded Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 22 Sep 2023 10:20:01 +0200 Subject: [PATCH 35/61] Fix i18n keys --- .../service_dashboards/actions/save_dashboard_modal.tsx | 4 ++-- .../app/service_dashboards/empty_dashboards.tsx | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index b09601e66c614..485b16bf40207 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -253,13 +253,13 @@ function getLinkSuccessToastLabels(dashboardName: string) { function getEditSuccessToastLabels(dashboardName: string) { return { title: i18n.translate( - 'xpack.apm.serviceDashboards.linkSuccess.toast.title', + 'xpack.apm.serviceDashboards.editSuccess.toast.title', { defaultMessage: 'Edited "{dashboardName}" dashboard', values: { dashboardName }, } ), - text: i18n.translate('xpack.apm.serviceDashboards.linkSuccess.toast.text', { + text: i18n.translate('xpack.apm.serviceDashboards.editSuccess.toast.text', { defaultMessage: 'Your dashboard link have been updated', }), }; diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx index 423075fdb9ffb..843a2c47b2649 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/empty_dashboards.tsx @@ -63,9 +63,12 @@ export function EmptyDashboards({ actions }: Props) {

- {i18n.translate('xpack.apm.serviceDashboards.emptyTitle', { - defaultMessage: 'To get started, add your dashaboard', - })} + {i18n.translate( + 'xpack.apm.serviceDashboards.emptyBody.getStarted', + { + defaultMessage: 'To get started, add your dashaboard', + } + )}

} From 21813e86eeead83f2c66d11f6b99fb7a638cc8fd Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:50:03 +0000 Subject: [PATCH 36/61] [CI] Auto-commit changed files from 'node scripts/check_mappings_update --fix' --- .../current_mappings.json | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 83168a1815859..f68ec3135ebbf 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -3075,6 +3075,28 @@ } } }, + "apm-service-dashboard": { + "properties": { + "dashboardSavedObjectId": { + "type": "keyword" + }, + "dashboardTitle": { + "type": "text" + }, + "kuery": { + "type": "text" + }, + "useContextFilter": { + "type": "boolean" + }, + "linkTo": { + "type": "keyword" + }, + "serviceName": { + "type": "keyword" + } + } + }, "enterprise_search_telemetry": { "dynamic": false, "properties": {} From 5799238e0feeda426b2f6d48ce92cad12c989daa Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 22 Sep 2023 15:43:18 +0200 Subject: [PATCH 37/61] Rename SO to `apm-custom-dashboards` --- x-pack/plugins/apm/common/service_dashboards.ts | 2 +- .../routes/service_dashboards/get_service_dashboards.ts | 4 ++-- .../routes/service_dashboards/remove_service_dashboard.ts | 4 ++-- .../plugins/apm/server/routes/service_dashboards/route.ts | 6 +++--- .../routes/service_dashboards/save_service_dashboard.ts | 6 +++--- .../apm/server/saved_objects/apm_service_dashboards.ts | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index 9a66883422eb6..c6cd9eb9c9e1b 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -5,7 +5,7 @@ * 2.0. */ -export const APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE = 'apm-service-dashboard'; +export const APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE = 'apm-custom-dashboards'; // Define if the dashboard is linked to single or multiple services export enum DashboardTypeEnum { diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts index b488e0a96eca6..f02b60dc4ab42 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts @@ -7,7 +7,7 @@ import { SavedObjectsClientContract } from '@kbn/core/server'; import { - APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, SavedServiceDashboard, ServiceDashboard, } from '../../../common/service_dashboards'; @@ -22,7 +22,7 @@ export async function getServiceDashboards({ filter, }: Props): Promise { const result = await savedObjectsClient.find({ - type: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + type: APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, page: 1, perPage: 100, filter, diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts b/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts index 9a2250a11f325..2eca856b4333a 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts @@ -6,7 +6,7 @@ */ import { SavedObjectsClientContract } from '@kbn/core/server'; -import { APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE } from '../../../common/service_dashboards'; +import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../../common/service_dashboards'; interface Options { savedObjectsClient: SavedObjectsClientContract; @@ -17,7 +17,7 @@ export async function deleteServiceDashboard({ serviceDashboardId, }: Options) { return savedObjectsClient.delete( - APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, serviceDashboardId ); } diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index 4462b68902d24..9c2f8dabb9fdd 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { saveServiceDashbord } from './save_service_dashboard'; import { - APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, DashboardTypeEnum, SavedServiceDashboard, } from '../../../common/service_dashboards'; @@ -71,8 +71,8 @@ const serviceDashboardsRoute = createApmServerRoute({ const { context, params } = resources; const { serviceName } = params.path; - const soPrefixServiceName = `${APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE}.attributes.serviceName`; - const soPrefixLinkTo = `${APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE}.attributes.linkTo`; + const soPrefixServiceName = `${APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE}.attributes.serviceName`; + const soPrefixLinkTo = `${APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE}.attributes.linkTo`; const { savedObjects: { client: savedObjectsClient }, diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts index 04978da430010..b2ea9115d3ccd 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts @@ -7,7 +7,7 @@ import { SavedObjectsClientContract } from '@kbn/core/server'; import { - APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, SavedServiceDashboard, ServiceDashboard, } from '../../../common/service_dashboards'; @@ -28,12 +28,12 @@ export async function saveServiceDashbord({ updated_at: updatedAt, } = await (serviceDashboardId ? savedObjectsClient.update( - APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, serviceDashboardId, serviceDashboard ) : savedObjectsClient.create( - APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, serviceDashboard )); return { diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts index 3d79f3eca9a2a..d5db376682e6b 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts @@ -8,10 +8,10 @@ import { SavedObjectsType } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; -import { APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE } from '../../common/service_dashboards'; +import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../common/service_dashboards'; export const apmServiceDashboards: SavedObjectsType = { - name: APM_SERVICE_DASHBOARD_SAVED_OBJECT_TYPE, + name: APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, hidden: false, namespaceType: 'multiple', mappings: { From e0ce67000d30e2db77eab91fe1ea2605bd2e9126 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 22 Sep 2023 16:15:59 +0200 Subject: [PATCH 38/61] Remove not used attributes --- .../plugins/apm/common/service_dashboards.ts | 3 -- .../actions/save_dashboard_modal.tsx | 6 +-- x-pack/plugins/apm/server/plugin.ts | 4 +- .../get_service_dashboards.ts | 40 ------------------- ...dashboards.ts => apm_custom_dashboards.ts} | 8 +--- .../plugins/apm/server/saved_objects/index.ts | 2 +- 6 files changed, 6 insertions(+), 57 deletions(-) delete mode 100644 x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts rename x-pack/plugins/apm/server/saved_objects/{apm_service_dashboards.ts => apm_custom_dashboards.ts} (81%) diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index c6cd9eb9c9e1b..a2335bb4d7244 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -19,11 +19,8 @@ export type DashboardType = export interface ServiceDashboard { dashboardSavedObjectId: string; - dashboardTitle: string; useContextFilter: boolean; kuery?: string; - serviceName: string; - linkTo: DashboardType; } export interface SavedServiceDashboard extends ServiceDashboard { diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 485b16bf40207..734c596245647 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -29,6 +29,7 @@ import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plug import { useApmParams } from '../../../../hooks/use_apm_params'; import { DashboardTypeEnum } from '../../../../../common/service_dashboards'; import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; +import { SERVICE_NAME } from '../../../../../common/es_fields/apm'; interface Props { onClose: () => void; @@ -92,12 +93,9 @@ export function SaveDashboardModal({ params: { query: { serviceDashboardId: currentDashboard?.id }, body: { - dashboardTitle: newDashboard.label, dashboardSavedObjectId: newDashboard.value, useContextFilter, - linkTo: DashboardTypeEnum.single, // iteration-1: Only single supported - serviceName, - kuery: undefined, + kuery: `${SERVICE_NAME}: ${serviceName}`, }, }, signal: null, diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 685624401aba9..07010d5f8dc5b 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -32,7 +32,7 @@ import { apmTelemetry, apmServerSettings, apmServiceGroups, - apmServiceDashboards, + apmCustomDashboards, } from './saved_objects'; import { APMPluginSetup, @@ -76,7 +76,7 @@ export class APMPlugin core.savedObjects.registerType(apmTelemetry); core.savedObjects.registerType(apmServerSettings); core.savedObjects.registerType(apmServiceGroups); - core.savedObjects.registerType(apmServiceDashboards); + core.savedObjects.registerType(apmCustomDashboards); const currentConfig = this.initContext.config.get(); this.currentConfig = currentConfig; diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts deleted file mode 100644 index f02b60dc4ab42..0000000000000 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_service_dashboards.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SavedObjectsClientContract } from '@kbn/core/server'; -import { - APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, - SavedServiceDashboard, - ServiceDashboard, -} from '../../../common/service_dashboards'; - -interface Props { - savedObjectsClient: SavedObjectsClientContract; - filter: string; -} - -export async function getServiceDashboards({ - savedObjectsClient, - filter, -}: Props): Promise { - const result = await savedObjectsClient.find({ - type: APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, - page: 1, - perPage: 100, - filter, - sortField: 'updated_at', - sortOrder: 'desc', - }); - - return result.saved_objects.map( - ({ id, attributes, updated_at: upatedAt }) => ({ - id, - updatedAt: upatedAt ? Date.parse(upatedAt) : 0, - ...attributes, - }) - ); -} diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts similarity index 81% rename from x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts rename to x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts index d5db376682e6b..3560afd5f1610 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_service_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts @@ -10,18 +10,15 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../common/service_dashboards'; -export const apmServiceDashboards: SavedObjectsType = { +export const apmCustomDashboards: SavedObjectsType = { name: APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, hidden: false, namespaceType: 'multiple', mappings: { properties: { dashboardSavedObjectId: { type: 'keyword' }, - dashboardTitle: { type: 'text' }, kuery: { type: 'text' }, useContextFilter: { type: 'boolean' }, - linkTo: { type: 'keyword' }, - serviceName: { type: 'keyword' }, }, }, management: { @@ -38,11 +35,8 @@ export const apmServiceDashboards: SavedObjectsType = { schemas: { create: schema.object({ dashboardSavedObjectId: schema.string(), - dashboardTitle: schema.string(), kuery: schema.maybe(schema.string()), useContextFilter: schema.boolean(), - linkTo: schema.string(), - serviceName: schema.string(), }), }, }, diff --git a/x-pack/plugins/apm/server/saved_objects/index.ts b/x-pack/plugins/apm/server/saved_objects/index.ts index a598522b6af2c..effcedfc68932 100644 --- a/x-pack/plugins/apm/server/saved_objects/index.ts +++ b/x-pack/plugins/apm/server/saved_objects/index.ts @@ -8,4 +8,4 @@ export { apmTelemetry } from './apm_telemetry'; export { apmServerSettings } from './apm_server_settings'; export { apmServiceGroups } from './apm_service_groups'; -export { apmServiceDashboards } from './apm_service_dashboards'; +export { apmCustomDashboards } from './apm_custom_dashboards'; From a889f3dcf6c695c069e678a00e9c08c33540b799 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 22 Sep 2023 17:20:11 +0200 Subject: [PATCH 39/61] Fetch dashboard title dynamically --- .../actions/save_dashboard_modal.tsx | 10 ++--- .../actions/unlink_dashboard.tsx | 8 ++-- .../service_dashboards/dashboard_selector.tsx | 18 +++++---- .../app/service_dashboards/index.tsx | 37 +++++++++++++++---- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 734c596245647..72928c69186c3 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -27,15 +27,15 @@ import { useDashboardFetcher } from '../../../../hooks/use_dashboards_fetcher'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useApmParams } from '../../../../hooks/use_apm_params'; -import { DashboardTypeEnum } from '../../../../../common/service_dashboards'; import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; import { SERVICE_NAME } from '../../../../../common/es_fields/apm'; +import { MergedServiceDashboard } from '..'; interface Props { onClose: () => void; onRefresh: () => void; - currentDashboard?: SavedServiceDashboard; - serviceDashboards?: SavedServiceDashboard[]; + currentDashboard?: MergedServiceDashboard; + serviceDashboards?: MergedServiceDashboard[]; } export function SaveDashboardModal({ @@ -55,8 +55,8 @@ export function SaveDashboardModal({ ); if (currentDashboard) { - const { dashboardTitle, dashboardSavedObjectId } = currentDashboard; - defaultOption = { label: dashboardTitle, value: dashboardSavedObjectId }; + const { title, dashboardSavedObjectId } = currentDashboard; + defaultOption = { label: title, value: dashboardSavedObjectId }; } const [selectedDashboard, setSelectedDashboard] = useState( diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx index 6b3a78a645d64..812339909c7b2 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx @@ -7,7 +7,7 @@ import { EuiButtonEmpty, EuiConfirmModal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useCallback, useState } from 'react'; -import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; +import { MergedServiceDashboard } from '..'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { callApmApi } from '../../../../services/rest/create_call_apm_api'; @@ -15,7 +15,7 @@ export function UnlinkDashboard({ currentDashboard, onRefresh, }: { - currentDashboard: SavedServiceDashboard; + currentDashboard: MergedServiceDashboard; onRefresh: () => void; }) { const [isModalVisible, setIsModalVisible] = useState(false); @@ -36,7 +36,7 @@ export function UnlinkDashboard({ 'xpack.apm.serviceDashboards.unlinkSuccess.toast.title', { defaultMessage: 'Unlinked "{dashboardName}" dashboard', - values: { dashboardName: currentDashboard.dashboardTitle }, + values: { dashboardName: currentDashboard?.title }, } ), }); @@ -49,7 +49,7 @@ export function UnlinkDashboard({ { defaultMessage: 'Error while unlinking "{dashboardName}" dashboard', - values: { dashboardName: currentDashboard?.dashboardTitle }, + values: { dashboardName: currentDashboard?.title }, } ), text: error.body.message, diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 7165d1ac1adeb..5fb473d43807e 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -9,10 +9,11 @@ import React from 'react'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { SavedServiceDashboard } from '../../../../common/service_dashboards'; +import { MergedServiceDashboard } from '.'; interface Props { - serviceDashboards: SavedServiceDashboard[]; - currentDashboard?: SavedServiceDashboard; + serviceDashboards: MergedServiceDashboard[]; + currentDashboard?: MergedServiceDashboard; handleOnChange: (selectedId?: string) => void; } @@ -38,17 +39,18 @@ export function DashboardSelector({ } )} singleSelection={{ asPlainText: true }} - options={serviceDashboards.map( - ({ dashboardSavedObjectId, dashboardTitle }) => { - return { label: dashboardTitle, value: dashboardSavedObjectId }; - } - )} + options={serviceDashboards.map(({ dashboardSavedObjectId, title }) => { + return { + label: title, + value: dashboardSavedObjectId, + }; + })} selectedOptions={ currentDashboard ? [ { value: currentDashboard?.dashboardSavedObjectId, - label: currentDashboard?.dashboardTitle, + label: currentDashboard?.title, }, ] : [] diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index b2a4861e45562..354e10102f16b 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -33,6 +33,11 @@ import { EditDashboard } from './actions/edit_dashboard'; import { DashboardSelector } from './dashboard_selector'; import { useApmDataView } from '../../../hooks/use_apm_data_view'; import { getFilters } from '../metrics/static_dashboard'; +import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; + +export interface MergedServiceDashboard extends SavedServiceDashboard { + title: string; +} export function ServiceDashboards() { const { @@ -40,8 +45,15 @@ export function ServiceDashboards() { query: { environment, kuery, rangeFrom, rangeTo }, } = useApmParams('/services/{serviceName}/dashboards'); const [dashboard, setDashboard] = useState(); + const [serviceDashboards, setServiceDashboards] = + useState(); const [currentDashboard, setCurrentDashboard] = - useState(); + useState(); + const { data: allAvailableDashboards, status: dashboardsFetcherStatus } = + useDashboardFetcher(); + + console.log('allAvailableDashboards', allAvailableDashboards); + const { dataView } = useApmDataView(); const { data, status, refetch } = useFetcher( @@ -60,12 +72,23 @@ export function ServiceDashboards() { [serviceName] ); - const serviceDashboards = data?.serviceDashboards ?? []; + console.log('serviceDashboards', serviceDashboards); useEffect(() => { + const serviceDashboards = data?.serviceDashboards.map((dashboard) => ({ + title: + allAvailableDashboards.find( + ({ id }) => id === dashboard.dashboardSavedObjectId + )?.attributes.title ?? dashboard.id, + ...dashboard, + })); + + setServiceDashboards(serviceDashboards ?? []); // preselect dashboard - setCurrentDashboard(data?.serviceDashboards[0]); - }, [serviceDashboards]); + setCurrentDashboard(serviceDashboards[0]); + }, [allAvailableDashboards, data]); + + console.log('current', currentDashboard); const getCreationOptions = useCallback((): Promise => { @@ -98,7 +121,7 @@ export function ServiceDashboards() { const handleOnChange = (selectedId?: string) => { setCurrentDashboard( - serviceDashboards.find( + serviceDashboards?.find( ({ dashboardSavedObjectId }) => dashboardSavedObjectId === selectedId ) ); @@ -120,7 +143,7 @@ export function ServiceDashboards() { } /> - ) : status === FETCH_STATUS.SUCCESS && serviceDashboards.length > 0 ? ( + ) : status === FETCH_STATUS.SUCCESS && serviceDashboards?.length > 0 ? ( <> -

{currentDashboard?.dashboardTitle}

+

{currentDashboard?.title}

From dc83340e7851fe5a301a6cd367956ac6f9856615 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 22 Sep 2023 17:35:15 +0200 Subject: [PATCH 40/61] Show dashboard that exists --- .../app/service_dashboards/index.tsx | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 354e10102f16b..68abe162756b4 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -45,8 +45,9 @@ export function ServiceDashboards() { query: { environment, kuery, rangeFrom, rangeTo }, } = useApmParams('/services/{serviceName}/dashboards'); const [dashboard, setDashboard] = useState(); - const [serviceDashboards, setServiceDashboards] = - useState(); + const [serviceDashboards, setServiceDashboards] = useState< + MergedServiceDashboard[] + >([]); const [currentDashboard, setCurrentDashboard] = useState(); const { data: allAvailableDashboards, status: dashboardsFetcherStatus } = @@ -75,17 +76,27 @@ export function ServiceDashboards() { console.log('serviceDashboards', serviceDashboards); useEffect(() => { - const serviceDashboards = data?.serviceDashboards.map((dashboard) => ({ - title: - allAvailableDashboards.find( + const serviceDashboards = (data?.serviceDashboards ?? []).reduce( + (result, dashboard) => { + const matchedDashboard = allAvailableDashboards.find( ({ id }) => id === dashboard.dashboardSavedObjectId - )?.attributes.title ?? dashboard.id, - ...dashboard, - })); + ); + if (matchedDashboard) { + result.push({ + title: matchedDashboard.attributes.title ?? dashboard.id, + ...dashboard, + }); + } + return result; + }, + [] + ); - setServiceDashboards(serviceDashboards ?? []); + setServiceDashboards(serviceDashboards); // preselect dashboard - setCurrentDashboard(serviceDashboards[0]); + setCurrentDashboard( + serviceDashboards?.length > 0 ? serviceDashboards[0] : undefined + ); }, [allAvailableDashboards, data]); console.log('current', currentDashboard); From 09a02346deaecd3eb89e90f5c781cab11d292b16 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 25 Sep 2023 13:22:57 +0200 Subject: [PATCH 41/61] Get service that match dashboard kuery --- .../plugins/apm/common/service_dashboards.ts | 10 --- .../get_linked_custom_dashboards.ts | 37 ++++++++++ .../get_services_with_dashboards.ts | 72 +++++++++++++++++++ .../server/routes/service_dashboards/route.ts | 38 ++++------ 4 files changed, 124 insertions(+), 33 deletions(-) create mode 100644 x-pack/plugins/apm/server/routes/service_dashboards/get_linked_custom_dashboards.ts create mode 100644 x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index a2335bb4d7244..0b12083a55699 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -7,16 +7,6 @@ export const APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE = 'apm-custom-dashboards'; -// Define if the dashboard is linked to single or multiple services -export enum DashboardTypeEnum { - single = 'single', - multiple = 'multiple', -} - -export type DashboardType = - | DashboardTypeEnum.single - | DashboardTypeEnum.multiple; - export interface ServiceDashboard { dashboardSavedObjectId: string; useContextFilter: boolean; diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_linked_custom_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_linked_custom_dashboards.ts new file mode 100644 index 0000000000000..2d0d0c23f3d48 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_linked_custom_dashboards.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { + APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, + SavedServiceDashboard, + ServiceDashboard, +} from '../../../common/service_dashboards'; + +interface Props { + savedObjectsClient: SavedObjectsClientContract; +} + +export async function getLinkedCustomDashboards({ + savedObjectsClient, +}: Props): Promise { + const result = await savedObjectsClient.find({ + type: APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, + page: 1, + perPage: 1000, + sortField: 'updated_at', + sortOrder: 'desc', + }); + + return result.saved_objects.map( + ({ id, attributes, updated_at: upatedAt }) => ({ + id, + updatedAt: upatedAt ? Date.parse(upatedAt) : 0, + ...attributes, + }) + ); +} diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts new file mode 100644 index 0000000000000..6edf408b7f193 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kqlQuery, termQuery } from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { SERVICE_NAME } from '../../../common/es_fields/apm'; +import { + APMEventClient, + APMEventESSearchRequest, +} from '../../lib/helpers/create_es_client/create_apm_event_client'; +import { estypes } from '@elastic/elasticsearch'; +import { SavedServiceDashboard } from '../../../common/service_dashboards'; + +function getSearchRequest( + filters: estypes.QueryDslQueryContainer[] +): APMEventESSearchRequest { + return { + apm: { + events: [ProcessorEvent.metric, ProcessorEvent.transaction], + }, + body: { + track_total_hits: false, + terminate_after: 1, + size: 1, + query: { + bool: { + filter: filters, + }, + }, + }, + }; +} +export async function getServicesWithDashboards({ + apmEventClient, + allLinkedCustomDashboards, + serviceName, +}: { + apmEventClient: APMEventClient; + allLinkedCustomDashboards: SavedServiceDashboard[]; + serviceName: string; +}): Promise { + const allKueryPerDashboard = allLinkedCustomDashboards.map(({ kuery }) => ({ + kuery, + })); + const allSearches = allKueryPerDashboard.map((dashboard) => + getSearchRequest([ + ...kqlQuery(dashboard.kuery), + ...termQuery(SERVICE_NAME, serviceName), + ]) + ); + + const allResponses = ( + await apmEventClient.msearch('get_services_with_dashboards', ...allSearches) + ).responses; + + const filteredDashboards = []; + + for (let index = 0; index < allLinkedCustomDashboards.length; index++) { + const responsePerDashboard = allResponses[index]; + const dashboard = allLinkedCustomDashboards[index]; + + if (responsePerDashboard.hits.hits.length > 0) { + filteredDashboards.push(dashboard); + } + } + + return filteredDashboards; +} diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index 9c2f8dabb9fdd..e05532b5eb0ec 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -8,18 +8,11 @@ import * as t from 'io-ts'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { saveServiceDashbord } from './save_service_dashboard'; -import { - APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, - DashboardTypeEnum, - SavedServiceDashboard, -} from '../../../common/service_dashboards'; -import { getServiceDashboards } from './get_service_dashboards'; +import { SavedServiceDashboard } from '../../../common/service_dashboards'; import { deleteServiceDashboard } from './remove_service_dashboard'; - -const linkToRt = t.union([ - t.literal(DashboardTypeEnum.single), - t.literal(DashboardTypeEnum.multiple), -]); +import { getLinkedCustomDashboards } from './get_linked_custom_dashboards'; +import { getServicesWithDashboards } from './get_services_with_dashboards'; +import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; const serviceDashboardSaveRoute = createApmServerRoute({ endpoint: 'POST /internal/apm/service-dashboard', @@ -32,11 +25,8 @@ const serviceDashboardSaveRoute = createApmServerRoute({ ]), body: t.type({ dashboardSavedObjectId: t.string, - dashboardTitle: t.string, kuery: t.union([t.string, t.undefined]), - serviceName: t.string, useContextFilter: t.boolean, - linkTo: linkToRt, }), }), options: { tags: ['access:apm', 'access:apm_write'] }, @@ -71,21 +61,23 @@ const serviceDashboardsRoute = createApmServerRoute({ const { context, params } = resources; const { serviceName } = params.path; - const soPrefixServiceName = `${APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE}.attributes.serviceName`; - const soPrefixLinkTo = `${APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE}.attributes.linkTo`; + const apmEventClient = await getApmEventClient(resources); const { savedObjects: { client: savedObjectsClient }, } = await context.core; - const [serviceDashboards] = await Promise.all([ - getServiceDashboards({ - savedObjectsClient, - filter: `${soPrefixServiceName}:${serviceName} AND ${soPrefixLinkTo}: ${DashboardTypeEnum.single} `, - }), - ]); + const allLinkedCustomDashboards = await getLinkedCustomDashboards({ + savedObjectsClient, + }); + + const servicesWithDashboards = await getServicesWithDashboards({ + apmEventClient, + allLinkedCustomDashboards, + serviceName, + }); - return { serviceDashboards }; + return { serviceDashboards: servicesWithDashboards }; }, }); From f69c8a7f617a56dd554e768bad37c4a8f94ef520 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 25 Sep 2023 14:08:00 +0200 Subject: [PATCH 42/61] Update API tests --- .../tests/service_dashboards/api_helper.ts | 10 - .../service_dashboards.spec.ts | 197 ++++++++++++------ 2 files changed, 131 insertions(+), 76 deletions(-) diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts index b77ffb77dc245..9cdea47fa8d37 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { DashboardTypeEnum } from '@kbn/apm-plugin/common/service_dashboards'; import { ApmApiClient } from '../../common/config'; export async function getServiceDashboardApi(apmApiClient: ApmApiClient, serviceName: string) { @@ -19,21 +18,15 @@ export async function getServiceDashboardApi(apmApiClient: ApmApiClient, service export async function getLinkServiceDashboardApi({ dashboardSavedObjectId, - dashboardTitle, apmApiClient, serviceDashboardId, - serviceName, kuery, - linkTo, useContextFilter, }: { apmApiClient: ApmApiClient; dashboardSavedObjectId: string; - dashboardTitle: string; serviceDashboardId?: string; - serviceName: string; kuery: string; - linkTo: DashboardTypeEnum; useContextFilter: boolean; }) { const response = await apmApiClient.writeUser({ @@ -44,11 +37,8 @@ export async function getLinkServiceDashboardApi({ }, body: { dashboardSavedObjectId, - dashboardTitle, kuery, - serviceName, useContextFilter, - linkTo, }, }, }); diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts index 00c1f2aba15fc..ae3d8115d69e9 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts @@ -5,7 +5,8 @@ * 2.0. */ import expect from '@kbn/expect'; -import { DashboardTypeEnum } from '@kbn/apm-plugin/common/service_dashboards'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; + import { FtrProviderContext } from '../../common/ftr_provider_context'; import { getServiceDashboardApi, @@ -16,81 +17,145 @@ import { export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); + const synthtrace = getService('synthtraceEsClient'); - registry.when('Service dashboard link', { config: 'basic', archives: [] }, () => { - afterEach(async () => { - await deleteAllServiceDashboard(apmApiClient, 'synth'); - }); + const start = '2023-08-22T00:00:00.000Z'; + const end = '2023-08-22T00:15:00.000Z'; - it('creates a new service dashboard', async () => { - const serviceDashboard = { - serviceName: 'synth', - dashboardTitle: 'my dashboard', - dashboardSavedObjectId: 'dashboard-saved-object-id', - linkTo: DashboardTypeEnum.single, - useContextFilter: true, - kuery: '', - }; - const createResponse = await getLinkServiceDashboardApi({ - apmApiClient, - ...serviceDashboard, + registry.when( + 'Service dashboards when data is not loaded', + { config: 'basic', archives: [] }, + () => { + describe('when data is not loaded', () => { + it('handles empty state', async () => { + const response = await getServiceDashboardApi(apmApiClient, 'synth-go'); + expect(response.status).to.be(200); + expect(response.body.serviceDashboards).to.eql([]); + }); }); - expect(createResponse.status).to.be(200); - expect(createResponse.body).to.have.property('id'); - expect(createResponse.body).to.have.property('updatedAt'); - - expect(createResponse.body).to.have.property('serviceName', serviceDashboard.serviceName); - expect(createResponse.body).to.have.property( - 'dashboardTitle', - serviceDashboard.dashboardTitle - ); - - expect(createResponse.body).to.have.property( - 'dashboardSavedObjectId', - serviceDashboard.dashboardSavedObjectId - ); - expect(createResponse.body).to.have.property( - 'useContextFilter', - serviceDashboard.useContextFilter - ); - expect(createResponse.body).to.have.property('linkTo', serviceDashboard.linkTo); - - const serviceDashboardResponse = await getServiceDashboardApi(apmApiClient, 'synth'); - expect(serviceDashboardResponse.body.serviceDashboards.length).to.be(1); + } + ); + + registry.when('Service dashboards when data is loaded', { config: 'basic', archives: [] }, () => { + const range = timerange(new Date(start).getTime(), new Date(end).getTime()); + + const goInstance = apm + .service({ + name: 'synth-go', + environment: 'production', + agentName: 'go', + }) + .instance('go-instance'); + + const javaInstance = apm + .service({ + name: 'synth-java', + environment: 'production', + agentName: 'java', + }) + .instance('java-instance'); + + before(async () => { + return synthtrace.index([ + range + .interval('1s') + .rate(4) + .generator((timestamp) => + goInstance + .transaction({ transactionName: 'GET /api' }) + .timestamp(timestamp) + .duration(1000) + .success() + ), + range + .interval('1s') + .rate(4) + .generator((timestamp) => + javaInstance + .transaction({ transactionName: 'GET /api' }) + .timestamp(timestamp) + .duration(1000) + .success() + ), + ]); }); - it('updates the existing linked service dashboard', async () => { - const serviceDashboard = { - serviceName: 'synth', - dashboardTitle: 'my dashboard', - dashboardSavedObjectId: 'dashboard-saved-object-id', - linkTo: DashboardTypeEnum.single, - useContextFilter: true, - kuery: '', - }; - - await getLinkServiceDashboardApi({ - apmApiClient, - ...serviceDashboard, - }); + after(() => { + return synthtrace.clean(); + }); - const serviceDashboardResponse = await getServiceDashboardApi(apmApiClient, 'synth'); + afterEach(async () => { + await deleteAllServiceDashboard(apmApiClient, 'synth-go'); + }); + + describe('when data is not loaded', () => { + it('creates a new service dashboard', async () => { + const serviceDashboard = { + dashboardSavedObjectId: 'dashboard-saved-object-id', + useContextFilter: true, + kuery: 'service.name: synth-go', + }; + const createResponse = await getLinkServiceDashboardApi({ + apmApiClient, + ...serviceDashboard, + }); + expect(createResponse.status).to.be(200); + expect(createResponse.body).to.have.property('id'); + expect(createResponse.body).to.have.property('updatedAt'); + + expect(createResponse.body).to.have.property( + 'dashboardSavedObjectId', + serviceDashboard.dashboardSavedObjectId + ); + expect(createResponse.body).to.have.property('kuery', serviceDashboard.kuery); + expect(createResponse.body).to.have.property( + 'useContextFilter', + serviceDashboard.useContextFilter + ); - const updateResponse = await getLinkServiceDashboardApi({ - apmApiClient, - serviceDashboardId: serviceDashboardResponse.body.serviceDashboards[0].id, - ...serviceDashboard, - useContextFilter: true, + const dasboardForGoService = await getServiceDashboardApi(apmApiClient, 'synth-go'); + const dashboardForJavaService = await getServiceDashboardApi(apmApiClient, 'synth-java'); + expect(dashboardForJavaService.body.serviceDashboards.length).to.be(0); + expect(dasboardForGoService.body.serviceDashboards.length).to.be(1); }); - expect(updateResponse.status).to.be(200); + it('updates the existing linked service dashboard', async () => { + const serviceDashboard = { + dashboardSavedObjectId: 'dashboard-saved-object-id', + useContextFilter: true, + kuery: 'service.name: synth-go or agent.name: java', + }; + + await getLinkServiceDashboardApi({ + apmApiClient, + ...serviceDashboard, + }); + + const dasboardForGoService = await getServiceDashboardApi(apmApiClient, 'synth-go'); - const updatedServiceDashboardResponse = await getServiceDashboardApi(apmApiClient, 'synth'); - expect(updatedServiceDashboardResponse.body.serviceDashboards.length).to.be(1); - expect(updatedServiceDashboardResponse.body.serviceDashboards[0]).to.have.property( - 'useContextFilter', - true - ); + const updateResponse = await getLinkServiceDashboardApi({ + apmApiClient, + serviceDashboardId: dasboardForGoService.body.serviceDashboards[0].id, + ...serviceDashboard, + useContextFilter: true, + }); + + expect(updateResponse.status).to.be(200); + + const updateddasboardForGoService = await getServiceDashboardApi(apmApiClient, 'synth-go'); + expect(updateddasboardForGoService.body.serviceDashboards.length).to.be(1); + expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( + 'useContextFilter', + true + ); + expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( + 'kuery', + 'service.name: synth-go or agent.name: java' + ); + + const dashboardForJavaService = await getServiceDashboardApi(apmApiClient, 'synth-java'); + expect(dashboardForJavaService.body.serviceDashboards.length).to.be(1); + }); }); }); } From 2b5ae27afb76e5125f5acc8c68a51cb2315cf1a3 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:42:10 +0000 Subject: [PATCH 43/61] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../app/service_dashboards/actions/save_dashboard_modal.tsx | 1 - .../components/app/service_dashboards/dashboard_selector.tsx | 1 - .../routes/service_dashboards/get_services_with_dashboards.ts | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 72928c69186c3..d5d8051f43c3e 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -27,7 +27,6 @@ import { useDashboardFetcher } from '../../../../hooks/use_dashboards_fetcher'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useApmParams } from '../../../../hooks/use_apm_params'; -import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; import { SERVICE_NAME } from '../../../../../common/es_fields/apm'; import { MergedServiceDashboard } from '..'; diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 5fb473d43807e..0e559ea6fed43 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SavedServiceDashboard } from '../../../../common/service_dashboards'; import { MergedServiceDashboard } from '.'; interface Props { diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts index 6edf408b7f193..4267e348cc6b6 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts @@ -7,12 +7,12 @@ import { kqlQuery, termQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { estypes } from '@elastic/elasticsearch'; import { SERVICE_NAME } from '../../../common/es_fields/apm'; import { APMEventClient, APMEventESSearchRequest, } from '../../lib/helpers/create_es_client/create_apm_event_client'; -import { estypes } from '@elastic/elasticsearch'; import { SavedServiceDashboard } from '../../../common/service_dashboards'; function getSearchRequest( From eb1f9205c388cb09a5b5d2cb904cb14720ff08a6 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 25 Sep 2023 15:05:03 +0200 Subject: [PATCH 44/61] Deep-link specific dashboard --- .../service_dashboards/dashboard_selector.tsx | 33 +++++++++++++++++-- .../app/service_dashboards/index.tsx | 23 ++++++------- .../routing/service_detail/index.tsx | 5 +++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 5fb473d43807e..0741d520213df 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; +import { useHistory } from 'react-router'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SavedServiceDashboard } from '../../../../common/service_dashboards'; import { MergedServiceDashboard } from '.'; +import { fromQuery, toQuery } from '../../shared/links/url_helpers'; interface Props { serviceDashboards: MergedServiceDashboard[]; @@ -22,6 +23,32 @@ export function DashboardSelector({ currentDashboard, handleOnChange, }: Props) { + const history = useHistory(); + + console.log('currentDashboard', currentDashboard); + + useEffect( + () => + history.push({ + ...history.location, + search: fromQuery({ + ...toQuery(location.search), + dashboardId: currentDashboard?.id, + }), + }), + [] + ); + + const onChange = (newDashboardId?: string) => { + history.push({ + ...history.location, + search: fromQuery({ + ...toQuery(location.search), + dashboardId: newDashboardId, + }), + }), + handleOnChange(); + }; return ( handleOnChange(newItem.value)} + onChange={([newItem]) => onChange(newItem.value)} isClearable={false} /> ); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 68abe162756b4..869fca3756bcd 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -42,7 +42,7 @@ export interface MergedServiceDashboard extends SavedServiceDashboard { export function ServiceDashboards() { const { path: { serviceName }, - query: { environment, kuery, rangeFrom, rangeTo }, + query: { environment, kuery, rangeFrom, rangeTo, dashboardId }, } = useApmParams('/services/{serviceName}/dashboards'); const [dashboard, setDashboard] = useState(); const [serviceDashboards, setServiceDashboards] = useState< @@ -50,10 +50,7 @@ export function ServiceDashboards() { >([]); const [currentDashboard, setCurrentDashboard] = useState(); - const { data: allAvailableDashboards, status: dashboardsFetcherStatus } = - useDashboardFetcher(); - - console.log('allAvailableDashboards', allAvailableDashboards); + const { data: allAvailableDashboards } = useDashboardFetcher(); const { dataView } = useApmDataView(); @@ -73,8 +70,6 @@ export function ServiceDashboards() { [serviceName] ); - console.log('serviceDashboards', serviceDashboards); - useEffect(() => { const serviceDashboards = (data?.serviceDashboards ?? []).reduce( (result, dashboard) => { @@ -83,7 +78,7 @@ export function ServiceDashboards() { ); if (matchedDashboard) { result.push({ - title: matchedDashboard.attributes.title ?? dashboard.id, + title: matchedDashboard.attributes.title, ...dashboard, }); } @@ -93,14 +88,16 @@ export function ServiceDashboards() { ); setServiceDashboards(serviceDashboards); + + const preselectedDashboard = + serviceDashboards.find( + ({ dashboardSavedObjectId }) => dashboardSavedObjectId === dashboardId + ) ?? serviceDashboards[0]; + // preselect dashboard - setCurrentDashboard( - serviceDashboards?.length > 0 ? serviceDashboards[0] : undefined - ); + setCurrentDashboard(preselectedDashboard); }, [allAvailableDashboards, data]); - console.log('current', currentDashboard); - const getCreationOptions = useCallback((): Promise => { const getInitialInput = () => ({ diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx index 17f49dc9b4538..56deaaa2e6d6e 100644 --- a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx @@ -385,6 +385,11 @@ export const serviceDetailRoute = { }), element: , }), + params: t.partial({ + query: t.partial({ + dashboardId: t.string, + }), + }), }, '/services/{serviceName}/': { element: , From 9d2e5738fa13e1ee5941a160ca0dfd8f9340a81e Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 25 Sep 2023 15:30:04 +0200 Subject: [PATCH 45/61] Fix eslint --- .../actions/save_dashboard_modal.tsx | 11 ++++++++++- .../actions/unlink_dashboard.tsx | 8 +++++++- .../service_dashboards/dashboard_selector.tsx | 10 +++++----- .../app/service_dashboards/index.tsx | 19 ++++++++++--------- .../public/hooks/use_dashboards_fetcher.ts | 2 +- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index d5d8051f43c3e..d59f1af36d42c 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -122,7 +122,16 @@ export function SaveDashboardModal({ } onClose(); }, - [selectedDashboard, notifications.toasts, useContextFilter] + [ + selectedDashboard, + notifications.toasts, + useContextFilter, + onClose, + reloadServiceDashboards, + isEditMode, + serviceName, + currentDashboard, + ] ); return ( diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx index 812339909c7b2..a6d6575daab2c 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx @@ -57,7 +57,13 @@ export function UnlinkDashboard({ } setIsModalVisible(!isModalVisible); }, - [currentDashboard, notifications.toasts] + [ + currentDashboard, + notifications.toasts, + setIsModalVisible, + onRefresh, + isModalVisible, + ] ); return ( <> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 0741d520213df..54f03f8ab2521 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect } from 'react'; -import { useHistory } from 'react-router'; +import { useHistory } from 'react-router-dom'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { MergedServiceDashboard } from '.'; @@ -25,8 +25,6 @@ export function DashboardSelector({ }: Props) { const history = useHistory(); - console.log('currentDashboard', currentDashboard); - useEffect( () => history.push({ @@ -36,10 +34,12 @@ export function DashboardSelector({ dashboardId: currentDashboard?.id, }), }), + // It should only update when loaded + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); - const onChange = (newDashboardId?: string) => { + function onChange(newDashboardId: string) { history.push({ ...history.location, search: fromQuery({ @@ -48,7 +48,7 @@ export function DashboardSelector({ }), }), handleOnChange(); - }; + } return ( { - const serviceDashboards = (data?.serviceDashboards ?? []).reduce( - (result, dashboard) => { + const filteredServiceDashbords = (data?.serviceDashboards ?? []).reduce( + (result, serviceDashboard) => { const matchedDashboard = allAvailableDashboards.find( - ({ id }) => id === dashboard.dashboardSavedObjectId + ({ id }) => id === serviceDashboard.dashboardSavedObjectId ); if (matchedDashboard) { result.push({ title: matchedDashboard.attributes.title, - ...dashboard, + ...serviceDashboard, }); } return result; @@ -87,16 +87,16 @@ export function ServiceDashboards() { [] ); - setServiceDashboards(serviceDashboards); + setServiceDashboards(filteredServiceDashbords); const preselectedDashboard = - serviceDashboards.find( + filteredServiceDashbords.find( ({ dashboardSavedObjectId }) => dashboardSavedObjectId === dashboardId - ) ?? serviceDashboards[0]; + ) ?? filteredServiceDashbords[0]; // preselect dashboard setCurrentDashboard(preselectedDashboard); - }, [allAvailableDashboards, data]); + }, [allAvailableDashboards, data?.serviceDashboards, dashboardId]); const getCreationOptions = useCallback((): Promise => { @@ -105,7 +105,8 @@ export function ServiceDashboards() { timeRange: { from: rangeFrom, to: rangeTo }, }); return Promise.resolve({ getInitialInput }); - }, []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [rangeFrom, rangeTo]); useEffect(() => { if (!dashboard) return; diff --git a/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts index 731628fa6ce53..c463d07276a3a 100644 --- a/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_dashboards_fetcher.ts @@ -51,6 +51,6 @@ export function useDashboardFetcher(query?: string): SearchDashboardsResult { } }; getDashboards(); - }, []); + }, [dashboard, query]); return result; } From e49b972c713a57a3b34180e493b75d49bb073908 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:39:11 +0000 Subject: [PATCH 46/61] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../components/app/service_dashboards/dashboard_selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 0741d520213df..419bbed68c2f1 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect } from 'react'; -import { useHistory } from 'react-router'; +import { useHistory } from 'react-router-dom'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { MergedServiceDashboard } from '.'; From acb2da75af5e838d2cb84e4e80b7a0ac9239c7e7 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 25 Sep 2023 15:48:12 +0200 Subject: [PATCH 47/61] Fix dashboard updateInput --- .../service_dashboards/dashboard_selector.tsx | 4 ++-- .../app/service_dashboards/index.tsx | 23 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 54f03f8ab2521..9ec28c8428bf8 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -46,8 +46,8 @@ export function DashboardSelector({ ...toQuery(location.search), dashboardId: newDashboardId, }), - }), - handleOnChange(); + }); + handleOnChange(); } return ( ({ getInitialInput }); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [rangeFrom, rangeTo]); useEffect(() => { if (!dashboard) return; - dashboard.updateInput({ - timeRange: { from: rangeFrom, to: rangeTo }, - query: { query: kuery, language: 'kuery' }, - }); - }, [kuery, dashboard, rangeFrom, rangeTo]); - - useEffect(() => { - if (!dashboard || !dataView) return; - dashboard.updateInput({ filters: dataView && currentDashboard?.useContextFilter ? getFilters(serviceName, environment, dataView) : [], + timeRange: { from: rangeFrom, to: rangeTo }, + query: { query: kuery, language: 'kuery' }, }); - }, [dataView, serviceName, environment, dashboard, currentDashboard]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + dataView, + serviceName, + environment, + kuery, + dashboard, + rangeFrom, + rangeTo, + ]); const handleOnChange = (selectedId?: string) => { setCurrentDashboard( From 1fef7bebaff97f7eb3f080ebbaf93568ed4a983c Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 27 Sep 2023 11:52:06 +0200 Subject: [PATCH 48/61] Rename useContextFilter to useServiceFiltet --- x-pack/plugins/apm/common/service_dashboards.ts | 2 +- .../actions/save_dashboard_modal.tsx | 12 ++++++------ .../components/app/service_dashboards/index.tsx | 2 +- .../apm/server/routes/service_dashboards/route.ts | 2 +- .../server/saved_objects/apm_custom_dashboards.ts | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/service_dashboards.ts index 0b12083a55699..a92be807b70b9 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/service_dashboards.ts @@ -9,7 +9,7 @@ export const APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE = 'apm-custom-dashboards'; export interface ServiceDashboard { dashboardSavedObjectId: string; - useContextFilter: boolean; + useServiceFilters: boolean; kuery?: string; } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index d59f1af36d42c..67ee5726d12d4 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -49,8 +49,8 @@ export function SaveDashboardModal({ const { data: allAvailableDashboards, status } = useDashboardFetcher(); let defaultOption: EuiComboBoxOptionOption | undefined; - const [useContextFilter, setUseContextFilter] = useState( - currentDashboard?.useContextFilter ?? true + const [useServiceFilters, setUseServiceFilters] = useState( + currentDashboard?.useServiceFilters ?? true ); if (currentDashboard) { @@ -93,7 +93,7 @@ export function SaveDashboardModal({ query: { serviceDashboardId: currentDashboard?.id }, body: { dashboardSavedObjectId: newDashboard.value, - useContextFilter, + useServiceFilters, kuery: `${SERVICE_NAME}: ${serviceName}`, }, }, @@ -125,7 +125,7 @@ export function SaveDashboardModal({ [ selectedDashboard, notifications.toasts, - useContextFilter, + useServiceFilters, onClose, reloadServiceDashboards, isEditMode, @@ -197,8 +197,8 @@ export function SaveDashboardModal({

} - onChange={() => setUseContextFilter(!useContextFilter)} - checked={useContextFilter} + onChange={() => setUseServiceFilters(!useServiceFilters)} + checked={useServiceFilters} />
diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 8c43267142c16..abeda8c7fe924 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -112,7 +112,7 @@ export function ServiceDashboards() { dashboard.updateInput({ filters: - dataView && currentDashboard?.useContextFilter + dataView && currentDashboard?.useServiceFilters ? getFilters(serviceName, environment, dataView) : [], timeRange: { from: rangeFrom, to: rangeTo }, diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index e05532b5eb0ec..25245a1b88e5b 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -26,7 +26,7 @@ const serviceDashboardSaveRoute = createApmServerRoute({ body: t.type({ dashboardSavedObjectId: t.string, kuery: t.union([t.string, t.undefined]), - useContextFilter: t.boolean, + useServiceFilters: t.boolean, }), }), options: { tags: ['access:apm', 'access:apm_write'] }, diff --git a/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts index 3560afd5f1610..0dfd3dd7a3a92 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts @@ -18,7 +18,7 @@ export const apmCustomDashboards: SavedObjectsType = { properties: { dashboardSavedObjectId: { type: 'keyword' }, kuery: { type: 'text' }, - useContextFilter: { type: 'boolean' }, + useServiceFilters: { type: 'boolean' }, }, }, management: { @@ -36,7 +36,7 @@ export const apmCustomDashboards: SavedObjectsType = { create: schema.object({ dashboardSavedObjectId: schema.string(), kuery: schema.maybe(schema.string()), - useContextFilter: schema.boolean(), + useServiceFilters: schema.boolean(), }), }, }, From 8ed3d8aedf51a52f659b201321bb497295230cfa Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 27 Sep 2023 11:53:13 +0200 Subject: [PATCH 49/61] Fix mapping --- .../current_mappings.json | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index f68ec3135ebbf..eca8ad5065bb4 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -3075,28 +3075,6 @@ } } }, - "apm-service-dashboard": { - "properties": { - "dashboardSavedObjectId": { - "type": "keyword" - }, - "dashboardTitle": { - "type": "text" - }, - "kuery": { - "type": "text" - }, - "useContextFilter": { - "type": "boolean" - }, - "linkTo": { - "type": "keyword" - }, - "serviceName": { - "type": "keyword" - } - } - }, "enterprise_search_telemetry": { "dynamic": false, "properties": {} @@ -3109,4 +3087,4 @@ "dynamic": false, "properties": {} } -} +} \ No newline at end of file From d3c66fc5e32b066d973e78d9c4d69fdb14acd2f1 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 27 Sep 2023 11:55:37 +0200 Subject: [PATCH 50/61] Make SO exportable --- .../plugins/apm/server/saved_objects/apm_custom_dashboards.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts index 0dfd3dd7a3a92..e2c8a8a13c391 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts @@ -22,7 +22,7 @@ export const apmCustomDashboards: SavedObjectsType = { }, }, management: { - importableAndExportable: false, + importableAndExportable: true, icon: 'apmApp', getTitle: () => i18n.translate('xpack.apm.apmServiceDashboards.title', { From 2e54123fb0d0eca08a4473f67863a75cc7ccd0a6 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Wed, 27 Sep 2023 17:05:08 +0200 Subject: [PATCH 51/61] Make sure we search only if there any linked dashboards --- .../actions/save_dashboard_modal.tsx | 7 +++--- .../get_services_with_dashboards.ts | 23 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 67ee5726d12d4..910c6e66f35d8 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -19,6 +19,7 @@ import { EuiFlexGroup, EuiToolTip, EuiIcon, + EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DashboardItem } from '@kbn/dashboard-plugin/common/content_management'; @@ -47,6 +48,7 @@ export function SaveDashboardModal({ core: { notifications }, } = useApmPluginContext(); const { data: allAvailableDashboards, status } = useDashboardFetcher(); + let defaultOption: EuiComboBoxOptionOption | undefined; const [useServiceFilters, setUseServiceFilters] = useState( @@ -204,10 +206,9 @@ export function SaveDashboardModal({ - {i18n.translate( 'xpack.apm.serviceDashboards.selectDashboard.cancel', @@ -215,7 +216,7 @@ export function SaveDashboardModal({ defaultMessage: 'Cancel', } )} - +
0) { + const allResponses = ( + await apmEventClient.msearch( + 'get_services_with_dashboards', + ...allSearches + ) + ).responses; + + for (let index = 0; index < allLinkedCustomDashboards.length; index++) { + const responsePerDashboard = allResponses[index]; + const dashboard = allLinkedCustomDashboards[index]; - if (responsePerDashboard.hits.hits.length > 0) { - filteredDashboards.push(dashboard); + if (responsePerDashboard.hits.hits.length > 0) { + filteredDashboards.push(dashboard); + } } } From fac14a633c8ddb76ea26c58f05e9764c15791d0d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:29:54 +0000 Subject: [PATCH 52/61] [CI] Auto-commit changed files from 'node scripts/check_mappings_update --fix' --- .../current_mappings.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index eca8ad5065bb4..75ea12660a8ca 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -3075,6 +3075,19 @@ } } }, + "apm-custom-dashboards": { + "properties": { + "dashboardSavedObjectId": { + "type": "keyword" + }, + "kuery": { + "type": "text" + }, + "useServiceFilters": { + "type": "boolean" + } + } + }, "enterprise_search_telemetry": { "dynamic": false, "properties": {} @@ -3087,4 +3100,4 @@ "dynamic": false, "properties": {} } -} \ No newline at end of file +} From 86ae0107d58a003fff5bdbb6b7763b450dddee49 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 29 Sep 2023 10:15:31 +0200 Subject: [PATCH 53/61] Pass timerange --- .../app/service_dashboards/index.tsx | 3 ++ .../get_services_with_dashboards.ts | 11 +++++- .../server/routes/service_dashboards/route.ts | 6 +++ .../tests/service_dashboards/api_helper.ts | 9 ++++- .../service_dashboards.spec.ts | 37 ++++++++++++++++--- 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index abeda8c7fe924..65d6d5906d562 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -34,6 +34,7 @@ import { DashboardSelector } from './dashboard_selector'; import { useApmDataView } from '../../../hooks/use_apm_data_view'; import { getFilters } from '../metrics/static_dashboard'; import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; +import { useTimeRange } from '../../../hooks/use_time_range'; export interface MergedServiceDashboard extends SavedServiceDashboard { title: string; @@ -51,6 +52,7 @@ export function ServiceDashboards() { const [currentDashboard, setCurrentDashboard] = useState(); const { data: allAvailableDashboards } = useDashboardFetcher(); + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); const { dataView } = useApmDataView(); @@ -62,6 +64,7 @@ export function ServiceDashboards() { { params: { path: { serviceName }, + query: { start, end }, }, } ); diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts b/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts index 1acfaf0e9d45f..a4c98b7a0d6c8 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { kqlQuery, termQuery } from '@kbn/observability-plugin/server'; +import { + kqlQuery, + rangeQuery, + termQuery, +} from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { estypes } from '@elastic/elasticsearch'; import { SERVICE_NAME } from '../../../common/es_fields/apm'; @@ -38,10 +42,14 @@ export async function getServicesWithDashboards({ apmEventClient, allLinkedCustomDashboards, serviceName, + start, + end, }: { apmEventClient: APMEventClient; allLinkedCustomDashboards: SavedServiceDashboard[]; serviceName: string; + start: number; + end: number; }): Promise { const allKueryPerDashboard = allLinkedCustomDashboards.map(({ kuery }) => ({ kuery, @@ -50,6 +58,7 @@ export async function getServicesWithDashboards({ getSearchRequest([ ...kqlQuery(dashboard.kuery), ...termQuery(SERVICE_NAME, serviceName), + ...rangeQuery(start, end), ]) ); diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts index 25245a1b88e5b..742025f3842fb 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/service_dashboards/route.ts @@ -13,6 +13,7 @@ import { deleteServiceDashboard } from './remove_service_dashboard'; import { getLinkedCustomDashboards } from './get_linked_custom_dashboards'; import { getServicesWithDashboards } from './get_services_with_dashboards'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; +import { rangeRt } from '../default_api_types'; const serviceDashboardSaveRoute = createApmServerRoute({ endpoint: 'POST /internal/apm/service-dashboard', @@ -51,6 +52,7 @@ const serviceDashboardsRoute = createApmServerRoute({ path: t.type({ serviceName: t.string, }), + query: rangeRt, }), options: { tags: ['access:apm'], @@ -59,6 +61,8 @@ const serviceDashboardsRoute = createApmServerRoute({ resources ): Promise<{ serviceDashboards: SavedServiceDashboard[] }> => { const { context, params } = resources; + const { start, end } = params.query; + const { serviceName } = params.path; const apmEventClient = await getApmEventClient(resources); @@ -75,6 +79,8 @@ const serviceDashboardsRoute = createApmServerRoute({ apmEventClient, allLinkedCustomDashboards, serviceName, + start, + end, }); return { serviceDashboards: servicesWithDashboards }; diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts index 9cdea47fa8d37..a93740bde5cfc 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts @@ -7,11 +7,18 @@ import { ApmApiClient } from '../../common/config'; -export async function getServiceDashboardApi(apmApiClient: ApmApiClient, serviceName: string) { +export async function getServiceDashboardApi( + apmApiClient: ApmApiClient, + serviceName: string, + start: number, + end: number +) { return apmApiClient.writeUser({ endpoint: 'GET /internal/apm/services/{serviceName}/dashboards', params: { path: { serviceName }, + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), }, }); } diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts index ae3d8115d69e9..be6555f7a1b97 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts @@ -28,7 +28,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { describe('when data is not loaded', () => { it('handles empty state', async () => { - const response = await getServiceDashboardApi(apmApiClient, 'synth-go'); + const response = await getServiceDashboardApi(apmApiClient, 'synth-go', start, end); expect(response.status).to.be(200); expect(response.body.serviceDashboards).to.eql([]); }); @@ -113,8 +113,18 @@ export default function ApiTest({ getService }: FtrProviderContext) { serviceDashboard.useContextFilter ); - const dasboardForGoService = await getServiceDashboardApi(apmApiClient, 'synth-go'); - const dashboardForJavaService = await getServiceDashboardApi(apmApiClient, 'synth-java'); + const dasboardForGoService = await getServiceDashboardApi( + apmApiClient, + 'synth-go', + start, + end + ); + const dashboardForJavaService = await getServiceDashboardApi( + apmApiClient, + 'synth-java', + start, + end + ); expect(dashboardForJavaService.body.serviceDashboards.length).to.be(0); expect(dasboardForGoService.body.serviceDashboards.length).to.be(1); }); @@ -131,7 +141,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { ...serviceDashboard, }); - const dasboardForGoService = await getServiceDashboardApi(apmApiClient, 'synth-go'); + const dasboardForGoService = await getServiceDashboardApi( + apmApiClient, + 'synth-go', + start, + end + ); const updateResponse = await getLinkServiceDashboardApi({ apmApiClient, @@ -142,7 +157,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(updateResponse.status).to.be(200); - const updateddasboardForGoService = await getServiceDashboardApi(apmApiClient, 'synth-go'); + const updateddasboardForGoService = await getServiceDashboardApi( + apmApiClient, + 'synth-go', + start, + end + ); expect(updateddasboardForGoService.body.serviceDashboards.length).to.be(1); expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( 'useContextFilter', @@ -153,7 +173,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { 'service.name: synth-go or agent.name: java' ); - const dashboardForJavaService = await getServiceDashboardApi(apmApiClient, 'synth-java'); + const dashboardForJavaService = await getServiceDashboardApi( + apmApiClient, + 'synth-java', + start, + end + ); expect(dashboardForJavaService.body.serviceDashboards.length).to.be(1); }); }); From 43f45450d7804ad1ee9e7152d38d4c8af2987c7d Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 29 Sep 2023 10:32:07 +0200 Subject: [PATCH 54/61] Fix checks --- .../apm/public/components/app/service_dashboards/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 65d6d5906d562..aa250e3cda32d 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -70,7 +70,7 @@ export function ServiceDashboards() { ); } }, - [serviceName] + [serviceName, start, end] ); useEffect(() => { From 14079de67599d7b8a7d37d83ecc9b9cf0330eb80 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 29 Sep 2023 12:06:15 +0200 Subject: [PATCH 55/61] Fix types --- .../app/service_dashboards/actions/edit_dashboard.tsx | 4 ++-- .../app/service_dashboards/actions/link_dashboard.tsx | 4 ++-- .../app/service_dashboards/dashboard_selector.tsx | 4 ++-- .../apm/public/components/app/service_dashboards/index.tsx | 6 +++++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx index 1aecc7ea814ff..e3a6619b446d6 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/edit_dashboard.tsx @@ -7,15 +7,15 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; import { SaveDashboardModal } from './save_dashboard_modal'; +import { MergedServiceDashboard } from '..'; export function EditDashboard({ onRefresh, currentDashboard, }: { onRefresh: () => void; - currentDashboard: SavedServiceDashboard; + currentDashboard: MergedServiceDashboard; }) { const [isModalVisible, setIsModalVisible] = useState(false); return ( diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx index 73c55c0f8b2ee..7b652c21039d8 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/link_dashboard.tsx @@ -7,7 +7,7 @@ import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; +import { MergedServiceDashboard } from '..'; import { SaveDashboardModal } from './save_dashboard_modal'; export function LinkDashboard({ @@ -17,7 +17,7 @@ export function LinkDashboard({ }: { onRefresh: () => void; emptyButton?: boolean; - serviceDashboards?: SavedServiceDashboard[]; + serviceDashboards?: MergedServiceDashboard[]; }) { const [isModalVisible, setIsModalVisible] = useState(false); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx index 9ec28c8428bf8..115b97ad41cc8 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/dashboard_selector.tsx @@ -39,7 +39,7 @@ export function DashboardSelector({ [] ); - function onChange(newDashboardId: string) { + function onChange(newDashboardId?: string) { history.push({ ...history.location, search: fromQuery({ @@ -47,7 +47,7 @@ export function DashboardSelector({ dashboardId: newDashboardId, }), }); - handleOnChange(); + handleOnChange(newDashboardId); } return ( { const filteredServiceDashbords = (data?.serviceDashboards ?? []).reduce( - (result, serviceDashboard) => { + ( + result: MergedServiceDashboard[], + serviceDashboard: SavedServiceDashboard + ) => { const matchedDashboard = allAvailableDashboards.find( ({ id }) => id === serviceDashboard.dashboardSavedObjectId ); From cdeeff28026b370fab9aafce97071b263a14d17b Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Fri, 29 Sep 2023 12:38:03 +0200 Subject: [PATCH 56/61] Fix types in testing --- .../tests/service_dashboards/api_helper.ts | 25 ++++++++++++------- .../service_dashboards.spec.ts | 14 +++++------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts index a93740bde5cfc..2251e888cfcb7 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts @@ -10,15 +10,17 @@ import { ApmApiClient } from '../../common/config'; export async function getServiceDashboardApi( apmApiClient: ApmApiClient, serviceName: string, - start: number, - end: number + start: string, + end: string ) { return apmApiClient.writeUser({ endpoint: 'GET /internal/apm/services/{serviceName}/dashboards', params: { path: { serviceName }, - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + }, }, }); } @@ -28,13 +30,13 @@ export async function getLinkServiceDashboardApi({ apmApiClient, serviceDashboardId, kuery, - useContextFilter, + useServiceFilters, }: { apmApiClient: ApmApiClient; dashboardSavedObjectId: string; serviceDashboardId?: string; kuery: string; - useContextFilter: boolean; + useServiceFilters: boolean; }) { const response = await apmApiClient.writeUser({ endpoint: 'POST /internal/apm/service-dashboard', @@ -45,15 +47,20 @@ export async function getLinkServiceDashboardApi({ body: { dashboardSavedObjectId, kuery, - useContextFilter, + useServiceFilters, }, }, }); return response; } -export async function deleteAllServiceDashboard(apmApiClient: ApmApiClient, serviceName: string) { - return await getServiceDashboardApi(apmApiClient, serviceName).then((response) => { +export async function deleteAllServiceDashboard( + apmApiClient: ApmApiClient, + serviceName: string, + start: string, + end: string +) { + return await getServiceDashboardApi(apmApiClient, serviceName, start, end).then((response) => { const promises = response.body.serviceDashboards.map((item) => { if (item.id) { return apmApiClient.writeUser({ diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts index be6555f7a1b97..042767f4fca71 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts @@ -85,14 +85,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); afterEach(async () => { - await deleteAllServiceDashboard(apmApiClient, 'synth-go'); + await deleteAllServiceDashboard(apmApiClient, 'synth-go', start, end); }); describe('when data is not loaded', () => { it('creates a new service dashboard', async () => { const serviceDashboard = { dashboardSavedObjectId: 'dashboard-saved-object-id', - useContextFilter: true, + useServiceFilters: true, kuery: 'service.name: synth-go', }; const createResponse = await getLinkServiceDashboardApi({ @@ -109,8 +109,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(createResponse.body).to.have.property('kuery', serviceDashboard.kuery); expect(createResponse.body).to.have.property( - 'useContextFilter', - serviceDashboard.useContextFilter + 'useServiceFilters', + serviceDashboard.useServiceFilters ); const dasboardForGoService = await getServiceDashboardApi( @@ -132,7 +132,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('updates the existing linked service dashboard', async () => { const serviceDashboard = { dashboardSavedObjectId: 'dashboard-saved-object-id', - useContextFilter: true, + useServiceFilters: true, kuery: 'service.name: synth-go or agent.name: java', }; @@ -152,7 +152,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { apmApiClient, serviceDashboardId: dasboardForGoService.body.serviceDashboards[0].id, ...serviceDashboard, - useContextFilter: true, + useServiceFilters: true, }); expect(updateResponse.status).to.be(200); @@ -165,7 +165,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(updateddasboardForGoService.body.serviceDashboards.length).to.be(1); expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( - 'useContextFilter', + 'useServiceFilters', true ); expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( From db629d8a18e5f414730d1fe5926ff4d5d23503a2 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 2 Oct 2023 13:00:10 +0200 Subject: [PATCH 57/61] Address feedback, rename to customDashboard --- .../current_mappings.json | 15 +---------- ...ice_dashboards.ts => custom_dashboards.ts} | 7 +++--- .../actions/goto_dashboard.tsx | 4 +-- .../actions/save_dashboard_modal.tsx | 25 +++++++++++-------- .../actions/unlink_dashboard.tsx | 4 +-- .../app/service_dashboards/index.tsx | 10 +++++--- .../get_global_apm_server_route_repository.ts | 2 +- .../get_linked_custom_dashboards.ts | 10 ++++---- .../get_services_with_dashboards.ts | 6 ++--- .../remove_service_dashboard.ts | 8 +++--- .../route.ts | 25 ++++++++++--------- .../save_service_dashboard.ts | 20 +++++++-------- .../saved_objects/apm_custom_dashboards.ts | 8 +++--- .../api_helper.ts | 19 +++++++------- .../custom_dashboards.spec.ts} | 18 +++++++------ 15 files changed, 91 insertions(+), 90 deletions(-) rename x-pack/plugins/apm/common/{service_dashboards.ts => custom_dashboards.ts} (69%) rename x-pack/plugins/apm/server/routes/{service_dashboards => custom_dashboards}/get_linked_custom_dashboards.ts (80%) rename x-pack/plugins/apm/server/routes/{service_dashboards => custom_dashboards}/get_services_with_dashboards.ts (92%) rename x-pack/plugins/apm/server/routes/{service_dashboards => custom_dashboards}/remove_service_dashboard.ts (85%) rename x-pack/plugins/apm/server/routes/{service_dashboards => custom_dashboards}/route.ts (81%) rename x-pack/plugins/apm/server/routes/{service_dashboards => custom_dashboards}/save_service_dashboard.ts (73%) rename x-pack/test/apm_api_integration/tests/{service_dashboards => custom_dashboards}/api_helper.ts (78%) rename x-pack/test/apm_api_integration/tests/{service_dashboards/service_dashboards.spec.ts => custom_dashboards/custom_dashboards.spec.ts} (92%) diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 6319d027398eb..0c8f3c7b3560e 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -3078,19 +3078,6 @@ } } }, - "apm-custom-dashboards": { - "properties": { - "dashboardSavedObjectId": { - "type": "keyword" - }, - "kuery": { - "type": "text" - }, - "useServiceFilters": { - "type": "boolean" - } - } - }, "enterprise_search_telemetry": { "dynamic": false, "properties": {} @@ -3103,4 +3090,4 @@ "dynamic": false, "properties": {} } -} +} \ No newline at end of file diff --git a/x-pack/plugins/apm/common/service_dashboards.ts b/x-pack/plugins/apm/common/custom_dashboards.ts similarity index 69% rename from x-pack/plugins/apm/common/service_dashboards.ts rename to x-pack/plugins/apm/common/custom_dashboards.ts index a92be807b70b9..7e289d970b2e6 100644 --- a/x-pack/plugins/apm/common/service_dashboards.ts +++ b/x-pack/plugins/apm/common/custom_dashboards.ts @@ -7,13 +7,14 @@ export const APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE = 'apm-custom-dashboards'; -export interface ServiceDashboard { +export interface ApmCustomDashboard { dashboardSavedObjectId: string; - useServiceFilters: boolean; + serviceNameFilterEnabled: boolean; + serviceEnvironmentFilterEnabled: boolean; kuery?: string; } -export interface SavedServiceDashboard extends ServiceDashboard { +export interface SavedApmCustomDashboard extends ApmCustomDashboard { id: string; updatedAt: number; } diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx index 7a5cb49aaae6c..f196077c41a4d 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/goto_dashboard.tsx @@ -9,12 +9,12 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { ApmPluginStartDeps } from '../../../../plugin'; -import { SavedServiceDashboard } from '../../../../../common/service_dashboards'; +import { SavedApmCustomDashboard } from '../../../../../common/custom_dashboards'; export function GotoDashboard({ currentDashboard, }: { - currentDashboard: SavedServiceDashboard; + currentDashboard: SavedApmCustomDashboard; }) { const { services: { diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx index 910c6e66f35d8..81dc0ba157a01 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/save_dashboard_modal.tsx @@ -51,8 +51,10 @@ export function SaveDashboardModal({ let defaultOption: EuiComboBoxOptionOption | undefined; - const [useServiceFilters, setUseServiceFilters] = useState( - currentDashboard?.useServiceFilters ?? true + const [serviceFiltersEnabled, setserviceFiltersEnabled] = useState( + (currentDashboard?.serviceEnvironmentFilterEnabled && + currentDashboard?.serviceNameFilterEnabled) ?? + true ); if (currentDashboard) { @@ -70,7 +72,7 @@ export function SaveDashboardModal({ path: { serviceName }, } = useApmParams('/services/{serviceName}/dashboards'); - const reloadServiceDashboards = useCallback(() => { + const reloadCustomDashboards = useCallback(() => { onRefresh(); }, [onRefresh]); @@ -90,12 +92,13 @@ export function SaveDashboardModal({ const [newDashboard] = selectedDashboard; try { if (newDashboard.value) { - await callApmApi('POST /internal/apm/service-dashboard', { + await callApmApi('POST /internal/apm/custom-dashboard', { params: { - query: { serviceDashboardId: currentDashboard?.id }, + query: { customDashboardId: currentDashboard?.id }, body: { dashboardSavedObjectId: newDashboard.value, - useServiceFilters, + serviceEnvironmentFilterEnabled: serviceFiltersEnabled, + serviceNameFilterEnabled: serviceFiltersEnabled, kuery: `${SERVICE_NAME}: ${serviceName}`, }, }, @@ -107,7 +110,7 @@ export function SaveDashboardModal({ ? getEditSuccessToastLabels(newDashboard.label) : getLinkSuccessToastLabels(newDashboard.label) ); - reloadServiceDashboards(); + reloadCustomDashboards(); } } catch (error) { console.error(error); @@ -127,9 +130,9 @@ export function SaveDashboardModal({ [ selectedDashboard, notifications.toasts, - useServiceFilters, + serviceFiltersEnabled, onClose, - reloadServiceDashboards, + reloadCustomDashboards, isEditMode, serviceName, currentDashboard, @@ -199,8 +202,8 @@ export function SaveDashboardModal({

} - onChange={() => setUseServiceFilters(!useServiceFilters)} - checked={useServiceFilters} + onChange={() => setserviceFiltersEnabled(!serviceFiltersEnabled)} + checked={serviceFiltersEnabled} /> diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx index a6d6575daab2c..b0dbda84bb6cf 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/actions/unlink_dashboard.tsx @@ -26,8 +26,8 @@ export function UnlinkDashboard({ const onConfirm = useCallback( async function () { try { - await callApmApi('DELETE /internal/apm/service-dashboard', { - params: { query: { serviceDashboardId: currentDashboard.id } }, + await callApmApi('DELETE /internal/apm/custom-dashboard', { + params: { query: { customDashboardId: currentDashboard.id } }, signal: null, }); diff --git a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx index 55aa9db872369..f5df58b95cc41 100644 --- a/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dashboards/index.tsx @@ -26,7 +26,7 @@ import { EmptyDashboards } from './empty_dashboards'; import { GotoDashboard, LinkDashboard } from './actions'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useApmParams } from '../../../hooks/use_apm_params'; -import { SavedServiceDashboard } from '../../../../common/service_dashboards'; +import { SavedApmCustomDashboard } from '../../../../common/custom_dashboards'; import { ContextMenu } from './context_menu'; import { UnlinkDashboard } from './actions/unlink_dashboard'; import { EditDashboard } from './actions/edit_dashboard'; @@ -36,7 +36,7 @@ import { getFilters } from '../metrics/static_dashboard'; import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher'; import { useTimeRange } from '../../../hooks/use_time_range'; -export interface MergedServiceDashboard extends SavedServiceDashboard { +export interface MergedServiceDashboard extends SavedApmCustomDashboard { title: string; } @@ -78,7 +78,7 @@ export function ServiceDashboards() { const filteredServiceDashbords = (data?.serviceDashboards ?? []).reduce( ( result: MergedServiceDashboard[], - serviceDashboard: SavedServiceDashboard + serviceDashboard: SavedApmCustomDashboard ) => { const matchedDashboard = allAvailableDashboards.find( ({ id }) => id === serviceDashboard.dashboardSavedObjectId @@ -119,7 +119,9 @@ export function ServiceDashboards() { dashboard.updateInput({ filters: - dataView && currentDashboard?.useServiceFilters + dataView && + currentDashboard?.serviceEnvironmentFilterEnabled && + currentDashboard?.serviceNameFilterEnabled ? getFilters(serviceName, environment, dataView) : [], timeRange: { from: rangeFrom, to: rangeTo }, diff --git a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts index 504659fdf2750..7c555366c9e68 100644 --- a/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts +++ b/x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts @@ -46,7 +46,7 @@ import { traceRouteRepository } from '../traces/route'; import { transactionRouteRepository } from '../transactions/route'; import { assistantRouteRepository } from '../assistant_functions/route'; import { profilingRouteRepository } from '../profiling/route'; -import { serviceDashboardsRouteRepository } from '../service_dashboards/route'; +import { serviceDashboardsRouteRepository } from '../custom_dashboards/route'; function getTypedGlobalApmServerRouteRepository() { const repository = { diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_linked_custom_dashboards.ts b/x-pack/plugins/apm/server/routes/custom_dashboards/get_linked_custom_dashboards.ts similarity index 80% rename from x-pack/plugins/apm/server/routes/service_dashboards/get_linked_custom_dashboards.ts rename to x-pack/plugins/apm/server/routes/custom_dashboards/get_linked_custom_dashboards.ts index 2d0d0c23f3d48..074ed7b206ec2 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_linked_custom_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/custom_dashboards/get_linked_custom_dashboards.ts @@ -8,9 +8,9 @@ import { SavedObjectsClientContract } from '@kbn/core/server'; import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, - SavedServiceDashboard, - ServiceDashboard, -} from '../../../common/service_dashboards'; + SavedApmCustomDashboard, + ApmCustomDashboard, +} from '../../../common/custom_dashboards'; interface Props { savedObjectsClient: SavedObjectsClientContract; @@ -18,8 +18,8 @@ interface Props { export async function getLinkedCustomDashboards({ savedObjectsClient, -}: Props): Promise { - const result = await savedObjectsClient.find({ +}: Props): Promise { + const result = await savedObjectsClient.find({ type: APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, page: 1, perPage: 1000, diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts b/x-pack/plugins/apm/server/routes/custom_dashboards/get_services_with_dashboards.ts similarity index 92% rename from x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts rename to x-pack/plugins/apm/server/routes/custom_dashboards/get_services_with_dashboards.ts index a4c98b7a0d6c8..23a77588eb6cd 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/get_services_with_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/custom_dashboards/get_services_with_dashboards.ts @@ -17,7 +17,7 @@ import { APMEventClient, APMEventESSearchRequest, } from '../../lib/helpers/create_es_client/create_apm_event_client'; -import { SavedServiceDashboard } from '../../../common/service_dashboards'; +import { SavedApmCustomDashboard } from '../../../common/custom_dashboards'; function getSearchRequest( filters: estypes.QueryDslQueryContainer[] @@ -46,11 +46,11 @@ export async function getServicesWithDashboards({ end, }: { apmEventClient: APMEventClient; - allLinkedCustomDashboards: SavedServiceDashboard[]; + allLinkedCustomDashboards: SavedApmCustomDashboard[]; serviceName: string; start: number; end: number; -}): Promise { +}): Promise { const allKueryPerDashboard = allLinkedCustomDashboards.map(({ kuery }) => ({ kuery, })); diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts b/x-pack/plugins/apm/server/routes/custom_dashboards/remove_service_dashboard.ts similarity index 85% rename from x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts rename to x-pack/plugins/apm/server/routes/custom_dashboards/remove_service_dashboard.ts index 2eca856b4333a..5a7a7b0d69e0e 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/remove_service_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/custom_dashboards/remove_service_dashboard.ts @@ -6,18 +6,18 @@ */ import { SavedObjectsClientContract } from '@kbn/core/server'; -import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../../common/service_dashboards'; +import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../../common/custom_dashboards'; interface Options { savedObjectsClient: SavedObjectsClientContract; - serviceDashboardId: string; + customDashboardId: string; } export async function deleteServiceDashboard({ savedObjectsClient, - serviceDashboardId, + customDashboardId, }: Options) { return savedObjectsClient.delete( APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, - serviceDashboardId + customDashboardId ); } diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts b/x-pack/plugins/apm/server/routes/custom_dashboards/route.ts similarity index 81% rename from x-pack/plugins/apm/server/routes/service_dashboards/route.ts rename to x-pack/plugins/apm/server/routes/custom_dashboards/route.ts index 742025f3842fb..084b92157b26e 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/custom_dashboards/route.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { saveServiceDashbord } from './save_service_dashboard'; -import { SavedServiceDashboard } from '../../../common/service_dashboards'; +import { SavedApmCustomDashboard } from '../../../common/custom_dashboards'; import { deleteServiceDashboard } from './remove_service_dashboard'; import { getLinkedCustomDashboards } from './get_linked_custom_dashboards'; import { getServicesWithDashboards } from './get_services_with_dashboards'; @@ -16,31 +16,32 @@ import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { rangeRt } from '../default_api_types'; const serviceDashboardSaveRoute = createApmServerRoute({ - endpoint: 'POST /internal/apm/service-dashboard', + endpoint: 'POST /internal/apm/custom-dashboard', params: t.type({ query: t.union([ t.partial({ - serviceDashboardId: t.string, + customDashboardId: t.string, }), t.undefined, ]), body: t.type({ dashboardSavedObjectId: t.string, kuery: t.union([t.string, t.undefined]), - useServiceFilters: t.boolean, + serviceNameFilterEnabled: t.boolean, + serviceEnvironmentFilterEnabled: t.boolean, }), }), options: { tags: ['access:apm', 'access:apm_write'] }, - handler: async (resources): Promise => { + handler: async (resources): Promise => { const { context, params } = resources; - const { serviceDashboardId } = params.query; + const { customDashboardId } = params.query; const { savedObjects: { client: savedObjectsClient }, } = await context.core; return saveServiceDashbord({ savedObjectsClient, - serviceDashboardId, + customDashboardId, serviceDashboard: params.body, }); }, @@ -59,7 +60,7 @@ const serviceDashboardsRoute = createApmServerRoute({ }, handler: async ( resources - ): Promise<{ serviceDashboards: SavedServiceDashboard[] }> => { + ): Promise<{ serviceDashboards: SavedApmCustomDashboard[] }> => { const { context, params } = resources; const { start, end } = params.query; @@ -88,20 +89,20 @@ const serviceDashboardsRoute = createApmServerRoute({ }); const serviceDashboardDeleteRoute = createApmServerRoute({ - endpoint: 'DELETE /internal/apm/service-dashboard', + endpoint: 'DELETE /internal/apm/custom-dashboard', params: t.type({ query: t.type({ - serviceDashboardId: t.string, + customDashboardId: t.string, }), }), options: { tags: ['access:apm', 'access:apm_write'] }, handler: async (resources): Promise => { const { context, params } = resources; - const { serviceDashboardId } = params.query; + const { customDashboardId } = params.query; const savedObjectsClient = (await context.core).savedObjects.client; await deleteServiceDashboard({ savedObjectsClient, - serviceDashboardId, + customDashboardId, }); }, }); diff --git a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts b/x-pack/plugins/apm/server/routes/custom_dashboards/save_service_dashboard.ts similarity index 73% rename from x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts rename to x-pack/plugins/apm/server/routes/custom_dashboards/save_service_dashboard.ts index b2ea9115d3ccd..5c43dda2a4da5 100644 --- a/x-pack/plugins/apm/server/routes/service_dashboards/save_service_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/custom_dashboards/save_service_dashboard.ts @@ -8,28 +8,28 @@ import { SavedObjectsClientContract } from '@kbn/core/server'; import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, - SavedServiceDashboard, - ServiceDashboard, -} from '../../../common/service_dashboards'; + SavedApmCustomDashboard, + ApmCustomDashboard, +} from '../../../common/custom_dashboards'; interface Options { savedObjectsClient: SavedObjectsClientContract; - serviceDashboardId?: string; - serviceDashboard: ServiceDashboard; + customDashboardId?: string; + serviceDashboard: ApmCustomDashboard; } export async function saveServiceDashbord({ savedObjectsClient, - serviceDashboardId, + customDashboardId, serviceDashboard, -}: Options): Promise { +}: Options): Promise { const { id, attributes, updated_at: updatedAt, - } = await (serviceDashboardId + } = await (customDashboardId ? savedObjectsClient.update( APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, - serviceDashboardId, + customDashboardId, serviceDashboard ) : savedObjectsClient.create( @@ -38,7 +38,7 @@ export async function saveServiceDashbord({ )); return { id, - ...(attributes as ServiceDashboard), + ...(attributes as ApmCustomDashboard), updatedAt: updatedAt ? Date.parse(updatedAt) : 0, }; } diff --git a/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts index e2c8a8a13c391..8d4b20757f136 100644 --- a/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts +++ b/x-pack/plugins/apm/server/saved_objects/apm_custom_dashboards.ts @@ -8,7 +8,7 @@ import { SavedObjectsType } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; -import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../common/service_dashboards'; +import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../common/custom_dashboards'; export const apmCustomDashboards: SavedObjectsType = { name: APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, @@ -18,7 +18,8 @@ export const apmCustomDashboards: SavedObjectsType = { properties: { dashboardSavedObjectId: { type: 'keyword' }, kuery: { type: 'text' }, - useServiceFilters: { type: 'boolean' }, + serviceEnvironmentFilterEnabled: { type: 'boolean' }, + serviceNameFilterEnabled: { type: 'boolean' }, }, }, management: { @@ -36,7 +37,8 @@ export const apmCustomDashboards: SavedObjectsType = { create: schema.object({ dashboardSavedObjectId: schema.string(), kuery: schema.maybe(schema.string()), - useServiceFilters: schema.boolean(), + serviceEnvironmentFilterEnabled: schema.boolean(), + serviceNameFilterEnabled: schema.boolean(), }), }, }, diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts b/x-pack/test/apm_api_integration/tests/custom_dashboards/api_helper.ts similarity index 78% rename from x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts rename to x-pack/test/apm_api_integration/tests/custom_dashboards/api_helper.ts index 2251e888cfcb7..a0fb0e976d109 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/custom_dashboards/api_helper.ts @@ -28,26 +28,27 @@ export async function getServiceDashboardApi( export async function getLinkServiceDashboardApi({ dashboardSavedObjectId, apmApiClient, - serviceDashboardId, + customDashboardId, kuery, - useServiceFilters, + serviceFiltersEnabled, }: { apmApiClient: ApmApiClient; dashboardSavedObjectId: string; - serviceDashboardId?: string; + customDashboardId?: string; kuery: string; - useServiceFilters: boolean; + serviceFiltersEnabled: boolean; }) { const response = await apmApiClient.writeUser({ - endpoint: 'POST /internal/apm/service-dashboard', + endpoint: 'POST /internal/apm/custom-dashboard', params: { query: { - serviceDashboardId, + customDashboardId, }, body: { dashboardSavedObjectId, kuery, - useServiceFilters, + serviceEnvironmentFilterEnabled: serviceFiltersEnabled, + serviceNameFilterEnabled: serviceFiltersEnabled, }, }, }); @@ -64,8 +65,8 @@ export async function deleteAllServiceDashboard( const promises = response.body.serviceDashboards.map((item) => { if (item.id) { return apmApiClient.writeUser({ - endpoint: 'DELETE /internal/apm/service-dashboard', - params: { query: { serviceDashboardId: item.id } }, + endpoint: 'DELETE /internal/apm/custom-dashboard', + params: { query: { customDashboardId: item.id } }, }); } }); diff --git a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts b/x-pack/test/apm_api_integration/tests/custom_dashboards/custom_dashboards.spec.ts similarity index 92% rename from x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts rename to x-pack/test/apm_api_integration/tests/custom_dashboards/custom_dashboards.spec.ts index 042767f4fca71..f8693cbce84ba 100644 --- a/x-pack/test/apm_api_integration/tests/service_dashboards/service_dashboards.spec.ts +++ b/x-pack/test/apm_api_integration/tests/custom_dashboards/custom_dashboards.spec.ts @@ -92,7 +92,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('creates a new service dashboard', async () => { const serviceDashboard = { dashboardSavedObjectId: 'dashboard-saved-object-id', - useServiceFilters: true, + serviceFiltersEnabled: true, kuery: 'service.name: synth-go', }; const createResponse = await getLinkServiceDashboardApi({ @@ -109,8 +109,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(createResponse.body).to.have.property('kuery', serviceDashboard.kuery); expect(createResponse.body).to.have.property( - 'useServiceFilters', - serviceDashboard.useServiceFilters + 'serviceFiltersEnabled', + serviceDashboard.serviceFiltersEnabled ); const dasboardForGoService = await getServiceDashboardApi( @@ -132,7 +132,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('updates the existing linked service dashboard', async () => { const serviceDashboard = { dashboardSavedObjectId: 'dashboard-saved-object-id', - useServiceFilters: true, + serviceFiltersEnabled: true, kuery: 'service.name: synth-go or agent.name: java', }; @@ -150,9 +150,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { const updateResponse = await getLinkServiceDashboardApi({ apmApiClient, - serviceDashboardId: dasboardForGoService.body.serviceDashboards[0].id, + customDashboardId: dasboardForGoService.body.serviceDashboards[0].id, ...serviceDashboard, - useServiceFilters: true, + serviceFiltersEnabled: true, }); expect(updateResponse.status).to.be(200); @@ -165,7 +165,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(updateddasboardForGoService.body.serviceDashboards.length).to.be(1); expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( - 'useServiceFilters', + 'serviceEnvironmentFilterEnabled', + true + ); + expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( + 'serviceNameFilterEnabled', true ); expect(updateddasboardForGoService.body.serviceDashboards[0]).to.have.property( From e8c8e2d3f650b21dc546ddac4c1db8433364f720 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 2 Oct 2023 11:19:52 +0000 Subject: [PATCH 58/61] [CI] Auto-commit changed files from 'node scripts/check_mappings_update --fix' --- .../current_mappings.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index b5e0ac55bef3c..4573165d839b7 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -3096,6 +3096,22 @@ } } }, + "apm-custom-dashboards": { + "properties": { + "dashboardSavedObjectId": { + "type": "keyword" + }, + "kuery": { + "type": "text" + }, + "serviceEnvironmentFilterEnabled": { + "type": "boolean" + }, + "serviceNameFilterEnabled": { + "type": "boolean" + } + } + }, "enterprise_search_telemetry": { "dynamic": false, "properties": {} @@ -3108,4 +3124,4 @@ "dynamic": false, "properties": {} } -} \ No newline at end of file +} From e91128461fa13ecbfedef923e360b66496c4ba14 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Mon, 2 Oct 2023 18:04:50 +0200 Subject: [PATCH 59/61] Fix tests --- .../tests/custom_dashboards/custom_dashboards.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/test/apm_api_integration/tests/custom_dashboards/custom_dashboards.spec.ts b/x-pack/test/apm_api_integration/tests/custom_dashboards/custom_dashboards.spec.ts index f8693cbce84ba..773e2bb06686d 100644 --- a/x-pack/test/apm_api_integration/tests/custom_dashboards/custom_dashboards.spec.ts +++ b/x-pack/test/apm_api_integration/tests/custom_dashboards/custom_dashboards.spec.ts @@ -109,7 +109,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(createResponse.body).to.have.property('kuery', serviceDashboard.kuery); expect(createResponse.body).to.have.property( - 'serviceFiltersEnabled', + 'serviceEnvironmentFilterEnabled', + serviceDashboard.serviceFiltersEnabled + ); + expect(createResponse.body).to.have.property( + 'serviceNameFilterEnabled', serviceDashboard.serviceFiltersEnabled ); From 869f7be945faedafc398eb194146cfc6b038677b Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 3 Oct 2023 10:13:51 +0200 Subject: [PATCH 60/61] Update snapshot --- .../migrations/group2/check_registered_types.test.ts | 1 + .../saved_objects/migrations/group3/type_registrations.test.ts | 1 + .../saved_objects/migrations/group5/dot_kibana_split.test.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 423008ee1eece..b5a1591e7a5bd 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -59,6 +59,7 @@ describe('checking migration metadata changes on all registered SO types', () => "action_task_params": "96e27e7f4e8273ffcd87060221e2b75e81912dd5", "alert": "dc710bc17dfc98a9a703d388569abccce5f8bf07", "api_key_pending_invalidation": "1399e87ca37b3d3a65d269c924eda70726cfe886", + "apm-custom-dashboards": "b67128f78160c288bd7efe25b2da6e2afd5e82fc", "apm-indices": "8a2d68d415a4b542b26b0d292034a28ffac6fed4", "apm-server-schema": "58a8c6468edae3d1dc520f0134f59cf3f4fd7eff", "apm-service-group": "66dfc1ddd40bad8f693c873bf6002ca30079a4ae", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index efb439e058cc2..2cef3801868bd 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -15,6 +15,7 @@ const previouslyRegisteredTypes = [ 'action_task_params', 'alert', 'api_key_pending_invalidation', + 'apm-custom-dashboards', 'apm-indices', 'apm-server-schema', 'apm-service-group', diff --git a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts index 3c41eafb6102c..c39ceaf30da69 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts @@ -181,6 +181,7 @@ describe('split .kibana index into multiple system indices', () => { "action_task_params", "alert", "api_key_pending_invalidation", + "apm-custom-dashboards", "apm-indices", "apm-server-schema", "apm-service-group", From 7cb024b1f7980c21776cf148fb5279f13ce58ce7 Mon Sep 17 00:00:00 2001 From: Kate Patticha Date: Tue, 3 Oct 2023 10:43:19 +0200 Subject: [PATCH 61/61] Rename getLinkedCustomDashboards to getCustomDashboards --- ...t_linked_custom_dashboards.ts => get_custom_dashboards.ts} | 2 +- x-pack/plugins/apm/server/routes/custom_dashboards/route.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename x-pack/plugins/apm/server/routes/custom_dashboards/{get_linked_custom_dashboards.ts => get_custom_dashboards.ts} (95%) diff --git a/x-pack/plugins/apm/server/routes/custom_dashboards/get_linked_custom_dashboards.ts b/x-pack/plugins/apm/server/routes/custom_dashboards/get_custom_dashboards.ts similarity index 95% rename from x-pack/plugins/apm/server/routes/custom_dashboards/get_linked_custom_dashboards.ts rename to x-pack/plugins/apm/server/routes/custom_dashboards/get_custom_dashboards.ts index 074ed7b206ec2..14a942cd26844 100644 --- a/x-pack/plugins/apm/server/routes/custom_dashboards/get_linked_custom_dashboards.ts +++ b/x-pack/plugins/apm/server/routes/custom_dashboards/get_custom_dashboards.ts @@ -16,7 +16,7 @@ interface Props { savedObjectsClient: SavedObjectsClientContract; } -export async function getLinkedCustomDashboards({ +export async function getCustomDashboards({ savedObjectsClient, }: Props): Promise { const result = await savedObjectsClient.find({ diff --git a/x-pack/plugins/apm/server/routes/custom_dashboards/route.ts b/x-pack/plugins/apm/server/routes/custom_dashboards/route.ts index 084b92157b26e..256cd2fb3cba9 100644 --- a/x-pack/plugins/apm/server/routes/custom_dashboards/route.ts +++ b/x-pack/plugins/apm/server/routes/custom_dashboards/route.ts @@ -10,7 +10,7 @@ import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { saveServiceDashbord } from './save_service_dashboard'; import { SavedApmCustomDashboard } from '../../../common/custom_dashboards'; import { deleteServiceDashboard } from './remove_service_dashboard'; -import { getLinkedCustomDashboards } from './get_linked_custom_dashboards'; +import { getCustomDashboards } from './get_custom_dashboards'; import { getServicesWithDashboards } from './get_services_with_dashboards'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { rangeRt } from '../default_api_types'; @@ -72,7 +72,7 @@ const serviceDashboardsRoute = createApmServerRoute({ savedObjects: { client: savedObjectsClient }, } = await context.core; - const allLinkedCustomDashboards = await getLinkedCustomDashboards({ + const allLinkedCustomDashboards = await getCustomDashboards({ savedObjectsClient, });