diff --git a/.github/workflows/on-merge.yml b/.github/workflows/on-merge.yml index 14f94571d551f..b41d04bf85174 100644 --- a/.github/workflows/on-merge.yml +++ b/.github/workflows/on-merge.yml @@ -10,21 +10,23 @@ jobs: name: 'Label and Backport' runs-on: ubuntu-latest if: | - github.event.pull_request.merged == true - && ( - ( - github.event.action == 'labeled' && ( - github.event.label.name == 'backport:prev-minor' - || github.event.label.name == 'backport:prev-major' - || github.event.label.name == 'backport:current-major' - || github.event.label.name == 'backport:all-open' - || github.event.label.name == 'backport:version' - || github.event.label.name == 'auto-backport' - ) - ) - || (github.event.action == 'unlabeled' && github.event.label.name == 'backport:skip') - || (github.event.action == 'closed') - ) + github.event.pull_request.merged == true && + (github.event.action == 'closed' || + (github.event.action == 'labeled' && + (github.event.label.name == 'backport:prev-minor' || + github.event.label.name == 'backport:prev-major' || + github.event.label.name == 'backport:current-major' || + github.event.label.name == 'backport:all-open' || + github.event.label.name == 'backport:version' || + github.event.label.name == 'auto-backport')) || + (github.event.action == 'unlabeled' && + github.event.label.name == 'backport:skip' && + (contains(github.event.pull_request.labels.*.name, 'backport:prev-minor') || + contains(github.event.pull_request.labels.*.name, 'backport:prev-major') || + contains(github.event.pull_request.labels.*.name, 'backport:current-major') || + contains(github.event.pull_request.labels.*.name, 'backport:all-open') || + contains(github.event.pull_request.labels.*.name, 'backport:version') || + contains(github.event.pull_request.labels.*.name, 'auto-backport')))) steps: - name: Checkout Actions uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/src/plugins/dashboard/jest_setup.ts b/src/plugins/dashboard/jest_setup.ts index 672e476fb852b..1b0de7854d3ac 100644 --- a/src/plugins/dashboard/jest_setup.ts +++ b/src/plugins/dashboard/jest_setup.ts @@ -7,8 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { setStubDashboardServices } from './public/services/mocks'; - /** * CAUTION: Be very mindful of the things you import in to this `jest_setup` file - anything that is imported * here (either directly or implicitly through dependencies) will be **unable** to be mocked elsewhere! @@ -16,4 +14,36 @@ import { setStubDashboardServices } from './public/services/mocks'; * Refer to the "Caution" section here: * https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options */ -setStubDashboardServices(); +import { + mockDashboardBackupService, + mockDashboardContentManagementCache, + mockDashboardContentManagementService, + setStubKibanaServices, +} from './public/services/mocks'; + +// Start the kibana services with stubs +setStubKibanaServices(); + +// Mock the dashboard services +jest.mock('./public/services/dashboard_content_management_service', () => { + return { + getDashboardContentManagementCache: () => mockDashboardContentManagementCache, + getDashboardContentManagementService: () => mockDashboardContentManagementService, + }; +}); + +jest.mock('./public/services/dashboard_backup_service', () => { + return { + getDashboardBackupService: () => mockDashboardBackupService, + }; +}); + +jest.mock('./public/services/dashboard_recently_accessed_service', () => { + return { + getDashboardRecentlyAccessedService: () => ({ + add: jest.fn(), + get: jest.fn(), + get$: jest.fn(), + }), + }; +}); diff --git a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx index 95eab08a83cc6..acd46f2763bbc 100644 --- a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx @@ -8,34 +8,36 @@ */ import React from 'react'; + +import { PresentationContainer } from '@kbn/presentation-containers'; import { - apiCanAccessViewMode, - apiHasLibraryTransforms, - EmbeddableApiContext, - getPanelTitle, - PublishesPanelTitle, CanAccessViewMode, - getInheritedViewMode, + EmbeddableApiContext, + HasInPlaceLibraryTransforms, HasLibraryTransforms, + HasParentApi, HasType, HasTypeDisplayName, - apiHasType, HasUniqueId, - HasParentApi, - apiHasUniqueId, - apiHasParentApi, - HasInPlaceLibraryTransforms, + PublishesPanelTitle, + apiCanAccessViewMode, apiHasInPlaceLibraryTransforms, + apiHasLibraryTransforms, + apiHasParentApi, + apiHasType, + apiHasUniqueId, + getInheritedViewMode, + getPanelTitle, } from '@kbn/presentation-publishing'; import { OnSaveProps, - SavedObjectSaveModal, SaveResult, + SavedObjectSaveModal, showSaveModal, } from '@kbn/saved-objects-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { PresentationContainer } from '@kbn/presentation-containers'; -import { pluginServices } from '../services/plugin_services'; + +import { coreServices } from '../services/kibana_services'; import { dashboardAddToLibraryActionStrings } from './_dashboard_actions_strings'; export const ACTION_ADD_TO_LIBRARY = 'saveToLibrary'; @@ -62,14 +64,6 @@ export class AddToLibraryAction implements Action { public readonly id = ACTION_ADD_TO_LIBRARY; public order = 8; - private toastsService; - - constructor() { - ({ - notifications: { toasts: this.toastsService }, - } = pluginServices.getServices()); - } - public getDisplayName({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); return dashboardAddToLibraryActionStrings.getDisplayName(); @@ -134,12 +128,12 @@ export class AddToLibraryAction implements Action { initialState: byRefState, }); } - this.toastsService.addSuccess({ + coreServices.notifications.toasts.addSuccess({ title: dashboardAddToLibraryActionStrings.getSuccessMessage(title ? `'${title}'` : ''), 'data-test-subj': 'addPanelToLibrarySuccess', }); } catch (e) { - this.toastsService.addDanger({ + coreServices.notifications.toasts.addDanger({ title: dashboardAddToLibraryActionStrings.getErrorMessage(title), 'data-test-subj': 'addPanelToLibraryError', }); diff --git a/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx b/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx index 856ef173168fc..5eec25f1f052b 100644 --- a/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx @@ -42,8 +42,6 @@ export class ClonePanelAction implements Action { public readonly id = ACTION_CLONE_PANEL; public order = 45; - constructor() {} - public getDisplayName({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); return dashboardClonePanelActionStrings.getDisplayName(); diff --git a/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_action.tsx b/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_action.tsx index 83762f407f423..fb31886919773 100644 --- a/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_action.tsx @@ -9,26 +9,26 @@ import React from 'react'; -import { CoreStart } from '@kbn/core-lifecycle-browser'; import { - apiIsOfType, - apiHasUniqueId, - apiHasParentApi, - apiPublishesSavedObjectId, - HasType, EmbeddableApiContext, - HasUniqueId, HasParentApi, + HasType, + HasUniqueId, PublishesSavedObjectId, + apiHasParentApi, + apiHasUniqueId, + apiIsOfType, + apiPublishesSavedObjectId, } from '@kbn/presentation-publishing'; import { toMountPoint } from '@kbn/react-kibana-mount'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { DASHBOARD_CONTAINER_TYPE } from '../dashboard_container'; import { DashboardApi } from '../dashboard_api/types'; -import { pluginServices } from '../services/plugin_services'; -import { CopyToDashboardModal } from './copy_to_dashboard_modal'; +import { DASHBOARD_CONTAINER_TYPE } from '../dashboard_container'; +import { coreServices } from '../services/kibana_services'; +import { getDashboardCapabilities } from '../utils/get_dashboard_capabilities'; import { dashboardCopyToDashboardActionStrings } from './_dashboard_actions_strings'; +import { CopyToDashboardModal } from './copy_to_dashboard_modal'; export const ACTION_COPY_TO_DASHBOARD = 'copyToDashboard'; @@ -60,16 +60,6 @@ export class CopyToDashboardAction implements Action { public readonly id = ACTION_COPY_TO_DASHBOARD; public order = 1; - private dashboardCapabilities; - private openModal; - - constructor(private core: CoreStart) { - ({ - dashboardCapabilities: this.dashboardCapabilities, - overlays: { openModal: this.openModal }, - } = pluginServices.getServices()); - } - public getDisplayName({ embeddable }: EmbeddableApiContext) { if (!apiIsCompatible(embeddable)) throw new IncompatibleActionError(); @@ -84,15 +74,15 @@ export class CopyToDashboardAction implements Action { public async isCompatible({ embeddable }: EmbeddableApiContext) { if (!apiIsCompatible(embeddable)) return false; const { createNew: canCreateNew, showWriteControls: canEditExisting } = - this.dashboardCapabilities; + getDashboardCapabilities(); return Boolean(canCreateNew || canEditExisting); } public async execute({ embeddable }: EmbeddableApiContext) { if (!apiIsCompatible(embeddable)) throw new IncompatibleActionError(); - const { theme, i18n } = this.core; - const session = this.openModal( + const { theme, i18n } = coreServices; + const session = coreServices.overlays.openModal( toMountPoint( session.close()} api={embeddable} />, { theme, i18n, diff --git a/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx b/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx index 06af2abbe0b0c..66b0ed367481d 100644 --- a/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx @@ -22,11 +22,12 @@ import { EmbeddablePackageState, PanelNotFoundError } from '@kbn/embeddable-plug import { apiHasSnapshottableState } from '@kbn/presentation-containers/interfaces/serialized_state'; import { LazyDashboardPicker, withSuspense } from '@kbn/presentation-util-plugin/public'; import { omit } from 'lodash'; -import React, { useCallback, useState } from 'react'; -import { createDashboardEditUrl, CREATE_NEW_DASHBOARD_URL } from '../dashboard_constants'; -import { pluginServices } from '../services/plugin_services'; -import { CopyToDashboardAPI } from './copy_to_dashboard_action'; +import React, { useCallback, useMemo, useState } from 'react'; +import { CREATE_NEW_DASHBOARD_URL, createDashboardEditUrl } from '../dashboard_constants'; +import { embeddableService } from '../services/kibana_services'; +import { getDashboardCapabilities } from '../utils/get_dashboard_capabilities'; import { dashboardCopyToDashboardActionStrings } from './_dashboard_actions_strings'; +import { CopyToDashboardAPI } from './copy_to_dashboard_action'; interface CopyToDashboardModalProps { api: CopyToDashboardAPI; @@ -36,11 +37,11 @@ interface CopyToDashboardModalProps { const DashboardPicker = withSuspense(LazyDashboardPicker); export function CopyToDashboardModal({ api, closeModal }: CopyToDashboardModalProps) { - const { - embeddable: { getStateTransfer }, - dashboardCapabilities: { createNew: canCreateNew, showWriteControls: canEditExisting }, - } = pluginServices.getServices(); - const stateTransfer = getStateTransfer(); + const stateTransfer = useMemo(() => embeddableService.getStateTransfer(), []); + const { createNew: canCreateNew, showWriteControls: canEditExisting } = useMemo( + () => getDashboardCapabilities(), + [] + ); const [dashboardOption, setDashboardOption] = useState<'new' | 'existing'>('existing'); const [selectedDashboard, setSelectedDashboard] = useState<{ id: string; name: string } | null>( diff --git a/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx b/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx index 8a375734ef1ef..b4f2a06e6895a 100644 --- a/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx @@ -31,8 +31,6 @@ export class ExpandPanelAction implements Action { public readonly id = ACTION_EXPAND_PANEL; public order = 7; - constructor() {} - public getDisplayName({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); return embeddable.parentApi.expandedPanelId.value diff --git a/src/plugins/dashboard/public/dashboard_actions/export_csv_action.tsx b/src/plugins/dashboard/public/dashboard_actions/export_csv_action.tsx index fa8f8640cbb3a..fd55816134ed1 100644 --- a/src/plugins/dashboard/public/dashboard_actions/export_csv_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/export_csv_action.tsx @@ -14,16 +14,16 @@ import { downloadMultipleAs } from '@kbn/share-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { - apiHasInspectorAdapters, HasInspectorAdapters, + apiHasInspectorAdapters, type Adapters, } from '@kbn/inspector-plugin/public'; import { EmbeddableApiContext, - getPanelTitle, PublishesPanelTitle, + getPanelTitle, } from '@kbn/presentation-publishing'; -import { pluginServices } from '../services/plugin_services'; +import { coreServices, fieldFormatService } from '../services/kibana_services'; import { dashboardExportCsvActionStrings } from './_dashboard_actions_strings'; export const ACTION_EXPORT_CSV = 'ACTION_EXPORT_CSV'; @@ -43,16 +43,6 @@ export class ExportCSVAction implements Action { public readonly type = ACTION_EXPORT_CSV; public readonly order = 18; // right after Export in discover which is 19 - private fieldFormats; - private uiSettings; - - constructor() { - ({ - data: { fieldFormats: this.fieldFormats }, - settings: { uiSettings: this.uiSettings }, - } = pluginServices.getServices()); - } - public getIconType() { return 'exportAction'; } @@ -70,9 +60,7 @@ export class ExportCSVAction implements Action { }; private getFormatter = (): FormatFactory | undefined => { - if (this.fieldFormats) { - return this.fieldFormats.deserialize; - } + return fieldFormatService.deserialize; }; private getDataTableContent = (adapters: Adapters | undefined) => { @@ -105,8 +93,8 @@ export class ExportCSVAction implements Action { memo[`${getPanelTitle(embeddable) || untitledFilename}${postFix}.csv`] = { content: exporters.datatableToCSV(datatable, { - csvSeparator: this.uiSettings.get('csv:separator', ','), - quoteValues: this.uiSettings.get('csv:quoteValues', true), + csvSeparator: coreServices.uiSettings.get('csv:separator', ','), + quoteValues: coreServices.uiSettings.get('csv:quoteValues', true), formatFactory, escapeFormulaValues: false, }), diff --git a/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.tsx b/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.tsx index ef179c006666f..854ff5da948f4 100644 --- a/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.tsx @@ -8,28 +8,28 @@ */ import React from 'react'; +import { merge } from 'rxjs'; import { isOfAggregateQueryType, isOfQueryType } from '@kbn/es-query'; import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; -import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; - import { - apiCanAccessViewMode, - apiPublishesPartialUnifiedSearch, - apiHasUniqueId, CanAccessViewMode, EmbeddableApiContext, - getInheritedViewMode, - getViewModeSubject, HasParentApi, - PublishesUnifiedSearch, HasUniqueId, PublishesDataViews, + PublishesUnifiedSearch, + apiCanAccessViewMode, + apiHasUniqueId, + apiPublishesPartialUnifiedSearch, + getInheritedViewMode, + getViewModeSubject, } from '@kbn/presentation-publishing'; -import { merge } from 'rxjs'; -import { pluginServices } from '../services/plugin_services'; -import { FiltersNotificationPopover } from './filters_notification_popover'; +import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; + +import { coreServices } from '../services/kibana_services'; import { dashboardFilterNotificationActionStrings } from './_dashboard_actions_strings'; +import { FiltersNotificationPopover } from './filters_notification_popover'; export const BADGE_FILTERS_NOTIFICATION = 'ACTION_FILTERS_NOTIFICATION'; @@ -58,18 +58,12 @@ export class FiltersNotificationAction implements Action { public readonly type = BADGE_FILTERS_NOTIFICATION; public readonly order = 2; - private settingsService; - - constructor() { - ({ settings: this.settingsService } = pluginServices.getServices()); - } - public readonly MenuItem = ({ context }: { context: EmbeddableApiContext }) => { const { embeddable } = context; if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ - uiSettings: this.settingsService.uiSettings, + uiSettings: coreServices.uiSettings, }); return ( diff --git a/src/plugins/dashboard/public/dashboard_actions/index.ts b/src/plugins/dashboard/public/dashboard_actions/index.ts index 30f6819bd37dd..55a371719d953 100644 --- a/src/plugins/dashboard/public/dashboard_actions/index.ts +++ b/src/plugins/dashboard/public/dashboard_actions/index.ts @@ -7,7 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { CoreStart } from '@kbn/core/public'; import { CONTEXT_MENU_TRIGGER, PANEL_NOTIFICATION_TRIGGER } from '@kbn/embeddable-plugin/public'; import { DashboardStartDependencies } from '../plugin'; import { AddToLibraryAction } from './add_to_library_action'; @@ -21,13 +20,11 @@ import { UnlinkFromLibraryAction } from './unlink_from_library_action'; import { LegacyUnlinkFromLibraryAction } from './legacy_unlink_from_library_action'; interface BuildAllDashboardActionsProps { - core: CoreStart; allowByValueEmbeddables?: boolean; plugins: DashboardStartDependencies; } export const buildAllDashboardActions = async ({ - core, plugins, allowByValueEmbeddables, }: BuildAllDashboardActionsProps) => { @@ -66,7 +63,7 @@ export const buildAllDashboardActions = async ({ uiActions.registerAction(legacyUnlinkFromLibraryAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, legacyUnlinkFromLibraryAction.id); - const copyToDashboardAction = new CopyToDashboardAction(core); + const copyToDashboardAction = new CopyToDashboardAction(); uiActions.registerAction(copyToDashboardAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, copyToDashboardAction.id); } diff --git a/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.test.tsx index 7495439e2164c..60367d7950d2e 100644 --- a/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.test.tsx @@ -9,11 +9,11 @@ import { PublishesViewMode, ViewMode } from '@kbn/presentation-publishing'; import { BehaviorSubject } from 'rxjs'; -import { pluginServices } from '../services/plugin_services'; import { LegacyAddToLibraryAction, LegacyAddPanelToLibraryActionApi, } from './legacy_add_to_library_action'; +import { coreServices } from '../services/kibana_services'; describe('Add to library action', () => { let action: LegacyAddToLibraryAction; @@ -62,7 +62,7 @@ describe('Add to library action', () => { it('shows a toast with a title from the API when successful', async () => { await action.execute(context); - expect(pluginServices.getServices().notifications.toasts.addSuccess).toHaveBeenCalledWith({ + expect(coreServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ 'data-test-subj': 'addPanelToLibrarySuccess', title: "Panel 'A very compatible API' was added to the library", }); @@ -71,7 +71,7 @@ describe('Add to library action', () => { it('shows a danger toast when the link operation is unsuccessful', async () => { context.embeddable.linkToLibrary = jest.fn().mockRejectedValue(new Error('Oh dang')); await action.execute(context); - expect(pluginServices.getServices().notifications.toasts.addDanger).toHaveBeenCalledWith({ + expect(coreServices.notifications.toasts.addDanger).toHaveBeenCalledWith({ 'data-test-subj': 'addPanelToLibraryError', title: 'An error was encountered adding panel A very compatible API to the library', }); diff --git a/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.tsx b/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.tsx index e48fa3a02bd13..dee049dc2874e 100644 --- a/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/legacy_add_to_library_action.tsx @@ -18,8 +18,9 @@ import { HasLegacyLibraryTransforms, } from '@kbn/presentation-publishing'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { pluginServices } from '../services/plugin_services'; + import { dashboardAddToLibraryActionStrings } from './_dashboard_actions_strings'; +import { coreServices } from '../services/kibana_services'; export const ACTION_LEGACY_ADD_TO_LIBRARY = 'legacySaveToLibrary'; @@ -35,14 +36,6 @@ export class LegacyAddToLibraryAction implements Action { public readonly id = ACTION_LEGACY_ADD_TO_LIBRARY; public order = 15; - private toastsService; - - constructor() { - ({ - notifications: { toasts: this.toastsService }, - } = pluginServices.getServices()); - } - public getDisplayName({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); return dashboardAddToLibraryActionStrings.getDisplayName(); @@ -63,14 +56,14 @@ export class LegacyAddToLibraryAction implements Action { const panelTitle = getPanelTitle(embeddable); try { await embeddable.linkToLibrary(); - this.toastsService.addSuccess({ + coreServices.notifications.toasts.addSuccess({ title: dashboardAddToLibraryActionStrings.getSuccessMessage( panelTitle ? `'${panelTitle}'` : '' ), 'data-test-subj': 'addPanelToLibrarySuccess', }); } catch (e) { - this.toastsService.addDanger({ + coreServices.notifications.toasts.addDanger({ title: dashboardAddToLibraryActionStrings.getErrorMessage(panelTitle), 'data-test-subj': 'addPanelToLibraryError', }); diff --git a/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.test.tsx index 135aac2cbf867..7a7e8836a305f 100644 --- a/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.test.tsx @@ -9,7 +9,7 @@ import { PublishesViewMode, ViewMode } from '@kbn/presentation-publishing'; import { BehaviorSubject } from 'rxjs'; -import { pluginServices } from '../services/plugin_services'; +import { coreServices } from '../services/kibana_services'; import { LegacyUnlinkFromLibraryAction, LegacyUnlinkPanelFromLibraryActionApi, @@ -61,7 +61,7 @@ describe('Unlink from library action', () => { it('shows a toast with a title from the API when successful', async () => { await action.execute(context); - expect(pluginServices.getServices().notifications.toasts.addSuccess).toHaveBeenCalledWith({ + expect(coreServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ 'data-test-subj': 'unlinkPanelSuccess', title: "Panel 'A very compatible API' is no longer connected to the library.", }); @@ -70,7 +70,7 @@ describe('Unlink from library action', () => { it('shows a danger toast when the link operation is unsuccessful', async () => { context.embeddable.unlinkFromLibrary = jest.fn().mockRejectedValue(new Error('Oh dang')); await action.execute(context); - expect(pluginServices.getServices().notifications.toasts.addDanger).toHaveBeenCalledWith({ + expect(coreServices.notifications.toasts.addDanger).toHaveBeenCalledWith({ 'data-test-subj': 'unlinkPanelFailure', title: "An error occured while unlinking 'A very compatible API' from the library.", }); diff --git a/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.tsx b/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.tsx index 40e2ca12e42c0..96daab215dec6 100644 --- a/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/legacy_unlink_from_library_action.tsx @@ -19,8 +19,8 @@ import { PublishesPanelTitle, HasLegacyLibraryTransforms, } from '@kbn/presentation-publishing'; -import { pluginServices } from '../services/plugin_services'; import { dashboardUnlinkFromLibraryActionStrings } from './_dashboard_actions_strings'; +import { coreServices } from '../services/kibana_services'; export const ACTION_LEGACY_UNLINK_FROM_LIBRARY = 'legacyUnlinkFromLibrary'; @@ -38,14 +38,6 @@ export class LegacyUnlinkFromLibraryAction implements Action { public readonly id = ACTION_UNLINK_FROM_LIBRARY; public order = 15; - private toastsService; - - constructor() { - ({ - notifications: { toasts: this.toastsService }, - } = pluginServices.getServices()); - } - public getDisplayName({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); return dashboardUnlinkFromLibraryActionStrings.getDisplayName(); @@ -107,12 +99,12 @@ export class UnlinkFromLibraryAction implements Action { } else { throw new IncompatibleActionError(); } - this.toastsService.addSuccess({ + coreServices.notifications.toasts.addSuccess({ title: dashboardUnlinkFromLibraryActionStrings.getSuccessMessage(title ? `'${title}'` : ''), 'data-test-subj': 'unlinkPanelSuccess', }); } catch (e) { - this.toastsService.addDanger({ + coreServices.notifications.toasts.addDanger({ title: dashboardUnlinkFromLibraryActionStrings.getFailureMessage(title ? `'${title}'` : ''), 'data-test-subj': 'unlinkPanelFailure', }); diff --git a/src/plugins/dashboard/public/dashboard_api/types.ts b/src/plugins/dashboard/public/dashboard_api/types.ts index 01d12c27ce443..bdd72afe9dc40 100644 --- a/src/plugins/dashboard/public/dashboard_api/types.ts +++ b/src/plugins/dashboard/public/dashboard_api/types.ts @@ -31,7 +31,7 @@ import { ControlGroupApi, ControlGroupSerializedState } from '@kbn/controls-plug import { Filter, Query, TimeRange } from '@kbn/es-query'; import { DefaultEmbeddableApi, ErrorEmbeddable, IEmbeddable } from '@kbn/embeddable-plugin/public'; import { DashboardPanelMap, DashboardPanelState } from '../../common'; -import { SaveDashboardReturn } from '../services/dashboard_content_management/types'; +import { SaveDashboardReturn } from '../services/dashboard_content_management_service/types'; import { DashboardStateFromSettingsFlyout, UnsavedPanelState } from '../dashboard_container/types'; export type DashboardApi = CanExpandPanels & diff --git a/src/plugins/dashboard/public/dashboard_app/dashboard_app.test.tsx b/src/plugins/dashboard/public/dashboard_app/dashboard_app.test.tsx index f04528e7bde2c..2bed3ce44ea61 100644 --- a/src/plugins/dashboard/public/dashboard_app/dashboard_app.test.tsx +++ b/src/plugins/dashboard/public/dashboard_app/dashboard_app.test.tsx @@ -7,22 +7,25 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { render, waitFor } from '@testing-library/react'; import { MemoryHistory, createMemoryHistory } from 'history'; import React, { useEffect } from 'react'; + +import { render, waitFor } from '@testing-library/react'; + +import { DashboardApi } from '..'; import type { DashboardRendererProps } from '../dashboard_container/external_api/dashboard_renderer'; +import { LazyDashboardRenderer } from '../dashboard_container/external_api/lazy_dashboard_renderer'; +import { DashboardTopNav } from '../dashboard_top_nav'; import { buildMockDashboard } from '../mocks'; +import { dataService } from '../services/kibana_services'; import { DashboardApp } from './dashboard_app'; -import * as dashboardRendererStuff from '../dashboard_container/external_api/lazy_dashboard_renderer'; -import { DashboardApi } from '..'; - -/* These tests circumvent the need to test the router and legacy code -/* the dashboard app will be passed the expanded panel id from the DashboardRouter through mountApp() -/* @link https://github.com/elastic/kibana/pull/190086/ -*/ +jest.mock('../dashboard_container/external_api/lazy_dashboard_renderer'); +jest.mock('../dashboard_top_nav'); describe('Dashboard App', () => { + dataService.query.filterManager.getFilters = jest.fn().mockImplementation(() => []); + const mockDashboard = buildMockDashboard(); let mockHistory: MemoryHistory; // this is in url_utils dashboardApi expandedPanel subscription @@ -35,19 +38,20 @@ describe('Dashboard App', () => { historySpy = jest.spyOn(mockHistory, 'replace'); /** - * Mock the LazyDashboardRenderer component to avoid rendering the actual dashboard + * Mock the DashboardTopNav + LazyDashboardRenderer component to avoid rendering the actual dashboard * and hitting errors that aren't relevant */ - jest - .spyOn(dashboardRendererStuff, 'LazyDashboardRenderer') - // we need overwrite the onApiAvailable prop to get the dashboard Api in the dashboard app - .mockImplementation(({ onApiAvailable }: DashboardRendererProps) => { + (DashboardTopNav as jest.Mock).mockImplementation(() => <>Top nav); + (LazyDashboardRenderer as jest.Mock).mockImplementation( + ({ onApiAvailable }: DashboardRendererProps) => { + // we need overwrite the onApiAvailable prop to get access to the dashboard API in this test useEffect(() => { onApiAvailable?.(mockDashboard as DashboardApi); }, [onApiAvailable]); return
Test renderer
; - }); + } + ); }); beforeEach(() => { diff --git a/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx b/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx index 759d817432c6a..ecb62bf8fc2b3 100644 --- a/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx +++ b/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx @@ -7,46 +7,52 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { v4 as uuidv4 } from 'uuid'; import { History } from 'history'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import useMount from 'react-use/lib/useMount'; import useObservable from 'react-use/lib/useObservable'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { debounceTime } from 'rxjs'; +import { v4 as uuidv4 } from 'uuid'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; import { createKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; -import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; -import { debounceTime } from 'rxjs'; -import { - DashboardAppNoDataPage, - isDashboardAppInNoDataState, -} from './no_data/dashboard_app_no_data'; -import { loadAndRemoveDashboardState, startSyncingExpandedPanelState } from './url/url_utils'; -import { - getSessionURLObservable, - getSearchSessionIdFromURL, - removeSearchSessionIdFromURL, - createSessionRestorationDataProvider, -} from './url/search_sessions_integration'; import { DashboardApi, DashboardRenderer } from '..'; -import { type DashboardEmbedSettings } from './types'; -import { pluginServices } from '../services/plugin_services'; -import { DashboardRedirect } from '../dashboard_container/types'; -import { useDashboardMountContext } from './hooks/dashboard_mount_context'; +import { SharedDashboardState } from '../../common'; import { - createDashboardEditUrl, DASHBOARD_APP_ID, DASHBOARD_STATE_STORAGE_KEY, + createDashboardEditUrl, } from '../dashboard_constants'; -import { useDashboardOutcomeValidation } from './hooks/use_dashboard_outcome_validation'; -import { loadDashboardHistoryLocationState } from './locator/load_dashboard_history_location_state'; import type { DashboardCreationOptions } from '../dashboard_container/embeddable/dashboard_container_factory'; +import { DashboardRedirect } from '../dashboard_container/types'; import { DashboardTopNav } from '../dashboard_top_nav'; -import { DashboardTabTitleSetter } from './tab_title_setter/dashboard_tab_title_setter'; +import { + coreServices, + dataService, + embeddableService, + screenshotModeService, + shareService, +} from '../services/kibana_services'; +import { useDashboardMountContext } from './hooks/dashboard_mount_context'; +import { useDashboardOutcomeValidation } from './hooks/use_dashboard_outcome_validation'; import { useObservabilityAIAssistantContext } from './hooks/use_observability_ai_assistant_context'; -import { SharedDashboardState } from '../../common'; +import { loadDashboardHistoryLocationState } from './locator/load_dashboard_history_location_state'; +import { + DashboardAppNoDataPage, + isDashboardAppInNoDataState, +} from './no_data/dashboard_app_no_data'; +import { DashboardTabTitleSetter } from './tab_title_setter/dashboard_tab_title_setter'; +import { type DashboardEmbedSettings } from './types'; +import { + createSessionRestorationDataProvider, + getSearchSessionIdFromURL, + getSessionURLObservable, + removeSearchSessionIdFromURL, +} from './url/search_sessions_integration'; +import { loadAndRemoveDashboardState, startSyncingExpandedPanelState } from './url/url_utils'; export interface DashboardAppProps { history: History; @@ -71,31 +77,15 @@ export function DashboardApp({ }); const [dashboardApi, setDashboardApi] = useState(undefined); - /** - * Unpack & set up dashboard services - */ - const { - screenshotMode: { isScreenshotMode, getScreenshotContext }, - coreContext: { executionContext }, - embeddable: { getStateTransfer }, - notifications: { toasts }, - settings: { uiSettings }, - data: { search, dataViews }, - customBranding, - share: { url }, - observabilityAIAssistant, - } = pluginServices.getServices(); - const showPlainSpinner = useObservable(customBranding.hasCustomBranding$, false); + const showPlainSpinner = useObservable(coreServices.customBranding.hasCustomBranding$, false); + const { scopedHistory: getScopedHistory } = useDashboardMountContext(); useObservabilityAIAssistantContext({ - observabilityAIAssistant: observabilityAIAssistant.start, dashboardApi, - search, - dataViews, }); - useExecutionContext(executionContext, { + useExecutionContext(coreServices.executionContext, { type: 'application', page: 'app', id: savedDashboardId || 'new', @@ -105,10 +95,10 @@ export function DashboardApp({ () => createKbnUrlStateStorage({ history, - useHash: uiSettings.get('state:storeInSessionStorage'), - ...withNotifyOnErrors(toasts), + useHash: coreServices.uiSettings.get('state:storeInSessionStorage'), + ...withNotifyOnErrors(coreServices.notifications.toasts), }), - [toasts, history, uiSettings] + [history] ); /** @@ -116,9 +106,9 @@ export function DashboardApp({ */ useEffect(() => { return () => { - search.session.clear(); + dataService.search.session.clear(); }; - }, [search.session]); + }, []); /** * Validate saved object load outcome @@ -141,7 +131,8 @@ export function DashboardApp({ ...stateFromLocator, // if print mode is active, force viewMode.PRINT - ...(isScreenshotMode() && getScreenshotContext('layout') === 'print' + ...(screenshotModeService.isScreenshotMode() && + screenshotModeService.getScreenshotContext('layout') === 'print' ? { viewMode: ViewMode.PRINT } : {}), }; @@ -149,7 +140,7 @@ export function DashboardApp({ return Promise.resolve({ getIncomingEmbeddable: () => - getStateTransfer().getIncomingEmbeddablePackage(DASHBOARD_APP_ID, true), + embeddableService.getStateTransfer().getIncomingEmbeddablePackage(DASHBOARD_APP_ID, true), // integrations useControlGroupIntegration: true, @@ -174,16 +165,7 @@ export function DashboardApp({ getCurrentPath: () => `#${createDashboardEditUrl(dashboardId)}`, }), }); - }, [ - history, - embedSettings, - validateOutcome, - getScopedHistory, - isScreenshotMode, - getStateTransfer, - kbnUrlStateStorage, - getScreenshotContext, - ]); + }, [history, embedSettings, validateOutcome, getScopedHistory, kbnUrlStateStorage]); useEffect(() => { if (!dashboardApi) return; @@ -208,7 +190,7 @@ export function DashboardApp({ return () => appStateSubscription.unsubscribe(); }, [dashboardApi, kbnUrlStateStorage, savedDashboardId]); - const locator = useMemo(() => url?.locators.get(DASHBOARD_APP_LOCATOR), [url]); + const locator = useMemo(() => shareService?.url.locators.get(DASHBOARD_APP_LOCATOR), []); return showNoDataPage ? ( setShowNoDataPage(false)} /> diff --git a/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx b/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx index 8449ed9de1080..6ceaede806fed 100644 --- a/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx +++ b/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx @@ -9,32 +9,33 @@ import './_dashboard_app.scss'; -import React from 'react'; -import { parse, ParsedQuery } from 'query-string'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { HashRouter, RouteComponentProps, Redirect } from 'react-router-dom'; -import { Routes, Route } from '@kbn/shared-ux-router'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { createKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { Route, Routes } from '@kbn/shared-ux-router'; +import { parse, ParsedQuery } from 'query-string'; +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { HashRouter, Redirect, RouteComponentProps } from 'react-router-dom'; import { - createDashboardListingFilterUrl, CREATE_NEW_DASHBOARD_URL, + createDashboardEditUrl, + createDashboardListingFilterUrl, DASHBOARD_APP_ID, LANDING_PAGE_PATH, VIEW_DASHBOARD_URL, } from '../dashboard_constants'; -import { DashboardApp } from './dashboard_app'; -import { pluginServices } from '../services/plugin_services'; import { RedirectToProps } from '../dashboard_container/types'; -import { createDashboardEditUrl } from '../dashboard_constants'; -import { DashboardNoMatch } from './listing_page/dashboard_no_match'; +import { coreServices, dataService, embeddableService } from '../services/kibana_services'; +import { getDashboardCapabilities } from '../utils/get_dashboard_capabilities'; +import { dashboardReadonlyBadge, getDashboardPageTitle } from './_dashboard_app_strings'; +import { DashboardApp } from './dashboard_app'; import { DashboardMountContext } from './hooks/dashboard_mount_context'; -import { DashboardEmbedSettings, DashboardMountContextProps } from './types'; import { DashboardListingPage } from './listing_page/dashboard_listing_page'; -import { dashboardReadonlyBadge, getDashboardPageTitle } from './_dashboard_app_strings'; +import { DashboardNoMatch } from './listing_page/dashboard_no_match'; +import { DashboardEmbedSettings, DashboardMountContextProps } from './types'; export const dashboardUrlParams = { showTopMenu: 'show-top-menu', @@ -56,24 +57,13 @@ export async function mountApp({ appUnMounted, mountContext, }: DashboardMountProps) { - const { - chrome: { setBadge, docTitle, setHelpExtension }, - dashboardCapabilities: { showWriteControls }, - documentationLinks: { dashboardDocLink }, - application: { navigateToApp }, - settings: { uiSettings }, - data: dataStart, - notifications, - embeddable, - } = pluginServices.getServices(); - let globalEmbedSettings: DashboardEmbedSettings | undefined; const getUrlStateStorage = (history: RouteComponentProps['history']) => createKbnUrlStateStorage({ history, - useHash: uiSettings.get('state:storeInSessionStorage'), - ...withNotifyOnErrors(notifications.toasts), + useHash: coreServices.uiSettings.get('state:storeInSessionStorage'), + ...withNotifyOnErrors(coreServices.notifications.toasts), }); const redirect = (redirectTo: RedirectToProps) => { @@ -87,7 +77,11 @@ export async function mountApp({ } else { path = createDashboardListingFilterUrl(redirectTo.filter); } - navigateToApp(DASHBOARD_APP_ID, { path: `#/${path}`, state, replace: redirectTo.useReplace }); + coreServices.application.navigateToApp(DASHBOARD_APP_ID, { + path: `#/${path}`, + state, + replace: redirectTo.useReplace, + }); }; const getDashboardEmbedSettings = ( @@ -120,7 +114,7 @@ export async function mountApp({ }; const renderListingPage = (routeProps: RouteComponentProps) => { - docTitle.change(getDashboardPageTitle()); + coreServices.chrome.docTitle.change(getDashboardPageTitle()); const routeParams = parse(routeProps.history.location.search); const title = (routeParams.title as string) || undefined; const filter = (routeParams.filter as string) || undefined; @@ -139,10 +133,10 @@ export async function mountApp({ }; const hasEmbeddableIncoming = Boolean( - embeddable.getStateTransfer().getIncomingEmbeddablePackage(DASHBOARD_APP_ID, false) + embeddableService.getStateTransfer().getIncomingEmbeddablePackage(DASHBOARD_APP_ID, false) ); if (!hasEmbeddableIncoming) { - dataStart.dataViews.clearCache(); + dataService.dataViews.clearCache(); } // dispatch synthetic hash change event to update hash history objects @@ -175,18 +169,18 @@ export async function mountApp({ ); - setHelpExtension({ + coreServices.chrome.setHelpExtension({ appName: getDashboardPageTitle(), links: [ { linkType: 'documentation', - href: `${dashboardDocLink}`, + href: `${coreServices.docLinks.links.dashboard.guide}`, }, ], }); - if (!showWriteControls) { - setBadge({ + if (!getDashboardCapabilities().showWriteControls) { + coreServices.chrome.setBadge({ text: dashboardReadonlyBadge.getText(), tooltip: dashboardReadonlyBadge.getTooltip(), iconType: 'glasses', @@ -194,7 +188,7 @@ export async function mountApp({ } render(app, element); return () => { - dataStart.search.session.clear(); + dataService.search.session.clear(); unlistenParentHistory(); unmountComponentAtNode(element); appUnMounted(); diff --git a/src/plugins/dashboard/public/dashboard_app/hooks/use_dashboard_outcome_validation.tsx b/src/plugins/dashboard/public/dashboard_app/hooks/use_dashboard_outcome_validation.tsx index 493c5b5f1c67e..fbcdb82ec3410 100644 --- a/src/plugins/dashboard/public/dashboard_app/hooks/use_dashboard_outcome_validation.tsx +++ b/src/plugins/dashboard/public/dashboard_app/hooks/use_dashboard_outcome_validation.tsx @@ -9,11 +9,11 @@ import { useCallback, useMemo, useState } from 'react'; -import { pluginServices } from '../../services/plugin_services'; +import { DashboardCreationOptions } from '../..'; import { createDashboardEditUrl } from '../../dashboard_constants'; +import { LoadDashboardReturn } from '../../services/dashboard_content_management_service/types'; +import { screenshotModeService, spacesService } from '../../services/kibana_services'; import { useDashboardMountContext } from './dashboard_mount_context'; -import { LoadDashboardReturn } from '../../services/dashboard_content_management/types'; -import { DashboardCreationOptions } from '../..'; export const useDashboardOutcomeValidation = () => { const [aliasId, setAliasId] = useState(); @@ -23,11 +23,6 @@ export const useDashboardOutcomeValidation = () => { const { scopedHistory: getScopedHistory } = useDashboardMountContext(); const scopedHistory = getScopedHistory?.(); - /** - * Unpack dashboard services - */ - const { screenshotMode, spaces } = pluginServices.getServices(); - const validateOutcome: DashboardCreationOptions['validateLoadedSavedObject'] = useCallback( ({ dashboardFound, resolveMeta, dashboardId }: LoadDashboardReturn) => { if (!dashboardFound) { @@ -41,10 +36,10 @@ export const useDashboardOutcomeValidation = () => { */ if (loadOutcome === 'aliasMatch' && dashboardId && alias) { const path = scopedHistory.location.hash.replace(dashboardId, alias); - if (screenshotMode.isScreenshotMode()) { + if (screenshotModeService.isScreenshotMode()) { scopedHistory.replace(path); // redirect without the toast when in screenshot mode. } else { - spaces.redirectLegacyUrl?.({ path, aliasPurpose }); + spacesService?.ui.redirectLegacyUrl({ path, aliasPurpose }); } return 'redirected'; // redirected. Stop loading dashboard. } @@ -54,20 +49,20 @@ export const useDashboardOutcomeValidation = () => { } return 'valid'; }, - [scopedHistory, screenshotMode, spaces] + [scopedHistory] ); const getLegacyConflictWarning = useMemo(() => { if (savedObjectId && outcome === 'conflict' && aliasId) { return () => - spaces.getLegacyUrlConflict?.({ + spacesService?.ui.components.getLegacyUrlConflict({ currentObjectId: savedObjectId, otherObjectId: aliasId, otherObjectPath: `#${createDashboardEditUrl(aliasId)}${scopedHistory.location.search}`, }); } return null; - }, [aliasId, outcome, savedObjectId, scopedHistory, spaces]); + }, [aliasId, outcome, savedObjectId, scopedHistory]); return { validateOutcome, getLegacyConflictWarning }; }; diff --git a/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx b/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx index 0348e68f77418..c20e8fcd1dc76 100644 --- a/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx +++ b/src/plugins/dashboard/public/dashboard_app/hooks/use_observability_ai_assistant_context.tsx @@ -7,28 +7,26 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public'; -import { useEffect } from 'react'; import { getESQLQueryColumns } from '@kbn/esql-utils'; -import type { ISearchStart } from '@kbn/data-plugin/public'; import { LensConfigBuilder, + LensDataset, type LensConfig, - type LensMetricConfig, - type LensPieConfig, type LensGaugeConfig, - type LensXYConfig, type LensHeatmapConfig, + type LensMetricConfig, type LensMosaicConfig, + type LensPieConfig, type LensRegionMapConfig, type LensTableConfig, type LensTagCloudConfig, type LensTreeMapConfig, - LensDataset, + type LensXYConfig, } from '@kbn/lens-embeddable-utils/config_builder'; -import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { LensEmbeddableInput } from '@kbn/lens-plugin/public'; +import { useEffect } from 'react'; import { DashboardApi } from '../../dashboard_api/types'; +import { dataService, observabilityAssistantService } from '../../services/kibana_services'; const chartTypes = [ 'xy', @@ -45,25 +43,19 @@ const chartTypes = [ ] as const; export function useObservabilityAIAssistantContext({ - observabilityAIAssistant, dashboardApi, - search, - dataViews, }: { - observabilityAIAssistant: ObservabilityAIAssistantPublicStart | undefined; dashboardApi: DashboardApi | undefined; - search: ISearchStart; - dataViews: DataViewsPublicPluginStart; }) { useEffect(() => { - if (!observabilityAIAssistant) { + if (!observabilityAssistantService) { return; } const { service: { setScreenContext }, createScreenContextAction, - } = observabilityAIAssistant; + } = observabilityAssistantService; return setScreenContext({ screenDescription: @@ -205,12 +197,12 @@ export function useObservabilityAIAssistantContext({ const [columns] = await Promise.all([ getESQLQueryColumns({ esqlQuery: query, - search: search.search, + search: dataService.search.search, signal, }), ]); - const configBuilder = new LensConfigBuilder(dataViews); + const configBuilder = new LensConfigBuilder(dataService.dataViews); let config: LensConfig; @@ -382,5 +374,5 @@ export function useObservabilityAIAssistantContext({ ] : [], }); - }, [observabilityAIAssistant, dashboardApi, search, dataViews]); + }, [dashboardApi]); } diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx index 4b0ad49ec9b14..5f41a0a872802 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx +++ b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx @@ -13,7 +13,6 @@ import { mount, ReactWrapper, ComponentType } from 'enzyme'; import { I18nProvider } from '@kbn/i18n-react'; import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; -import { pluginServices } from '../../services/plugin_services'; import { DashboardListingPage, DashboardListingPageProps } from './dashboard_listing_page'; // Mock child components. The Dashboard listing page mostly passes down props to shared UX components which are tested in their own packages. @@ -26,7 +25,12 @@ jest.mock('../../dashboard_listing/dashboard_listing', () => { }); import { DashboardAppNoDataPage } from '../no_data/dashboard_app_no_data'; +import { dataService } from '../../services/kibana_services'; +import { getDashboardContentManagementService } from '../../services/dashboard_content_management_service'; + +const dashboardContentManagementService = getDashboardContentManagementService(); const mockIsDashboardAppInNoDataState = jest.fn().mockResolvedValue(false); + jest.mock('../no_data/dashboard_app_no_data', () => { const originalModule = jest.requireActual('../no_data/dashboard_app_no_data'); return { @@ -59,9 +63,7 @@ function mountWith({ props: incomingProps }: { props?: DashboardListingPageProps test('renders analytics no data page when the user has no data view', async () => { mockIsDashboardAppInNoDataState.mockResolvedValueOnce(true); - pluginServices.getServices().data.dataViews.hasData.hasUserDataView = jest - .fn() - .mockResolvedValue(false); + dataService.dataViews.hasData.hasUserDataView = jest.fn().mockResolvedValue(false); let component: ReactWrapper; await act(async () => { @@ -93,9 +95,9 @@ test('When given a title that matches multiple dashboards, filter on the title', const props = makeDefaultProps(); props.title = title; - ( - pluginServices.getServices().dashboardContentManagement.findDashboards.findByTitle as jest.Mock - ).mockResolvedValue(undefined); + (dashboardContentManagementService.findDashboards.findByTitle as jest.Mock).mockResolvedValue( + undefined + ); let component: ReactWrapper; @@ -115,9 +117,9 @@ test('When given a title that matches one dashboard, redirect to dashboard', asy const title = 'search by title'; const props = makeDefaultProps(); props.title = title; - ( - pluginServices.getServices().dashboardContentManagement.findDashboards.findByTitle as jest.Mock - ).mockResolvedValue({ id: 'you_found_me' }); + (dashboardContentManagementService.findDashboards.findByTitle as jest.Mock).mockResolvedValue({ + id: 'you_found_me', + }); let component: ReactWrapper; diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx index 9b036ac98f6cb..034ee2f8e45f4 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx +++ b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx @@ -9,19 +9,20 @@ import React, { useEffect, useState } from 'react'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; import { syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import type { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; +import { DashboardRedirect } from '../../dashboard_container/types'; +import { DashboardListing } from '../../dashboard_listing/dashboard_listing'; +import { coreServices, dataService, serverlessService } from '../../services/kibana_services'; +import { getDashboardBreadcrumb } from '../_dashboard_app_strings'; import { DashboardAppNoDataPage, isDashboardAppInNoDataState, } from '../no_data/dashboard_app_no_data'; -import { pluginServices } from '../../services/plugin_services'; -import { getDashboardBreadcrumb } from '../_dashboard_app_strings'; -import { DashboardRedirect } from '../../dashboard_container/types'; import { getDashboardListItemLink } from './get_dashboard_list_item_link'; -import { DashboardListing } from '../../dashboard_listing/dashboard_listing'; +import { getDashboardContentManagementService } from '../../services/dashboard_content_management_service'; export interface DashboardListingPageProps { kbnUrlStateStorage: IKbnUrlStateStorage; @@ -36,13 +37,6 @@ export const DashboardListingPage = ({ initialFilter, kbnUrlStateStorage, }: DashboardListingPageProps) => { - const { - data: { query }, - serverless, - chrome: { setBreadcrumbs }, - dashboardContentManagement: { findDashboards }, - } = pluginServices.getServices(); - const [showNoDataPage, setShowNoDataPage] = useState(); useEffect(() => { let isMounted = true; @@ -56,40 +50,42 @@ export const DashboardListingPage = ({ }, []); useEffect(() => { - setBreadcrumbs([ + coreServices.chrome.setBreadcrumbs([ { text: getDashboardBreadcrumb(), }, ]); - if (serverless?.setBreadcrumbs) { + if (serverlessService) { // if serverless breadcrumbs available, // reset any deeper context breadcrumbs to only keep the main "dashboard" part that comes from the navigation config - serverless.setBreadcrumbs([]); + serverlessService.setBreadcrumbs([]); } - }, [setBreadcrumbs, serverless]); + }, []); useEffect(() => { // syncs `_g` portion of url with query services const { stop: stopSyncingQueryServiceStateWithUrl } = syncGlobalQueryStateWithUrl( - query, + dataService.query, kbnUrlStateStorage ); if (title) { - findDashboards.findByTitle(title).then((result) => { - if (!result) return; - redirectTo({ - destination: 'dashboard', - id: result.id, - useReplace: true, + getDashboardContentManagementService() + .findDashboards.findByTitle(title) + .then((result) => { + if (!result) return; + redirectTo({ + destination: 'dashboard', + id: result.id, + useReplace: true, + }); }); - }); } return () => { stopSyncingQueryServiceStateWithUrl(); }; - }, [title, redirectTo, query, kbnUrlStateStorage, findDashboards]); + }, [title, redirectTo, kbnUrlStateStorage]); const titleFilter = title ? `${title}` : ''; diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_no_match.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_no_match.tsx index b89f993546cd3..3ad35d34b7fd1 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_no_match.tsx +++ b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_no_match.tsx @@ -10,29 +10,23 @@ import React, { useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { i18n } from '@kbn/i18n'; import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { toMountPoint } from '@kbn/react-kibana-mount'; import { LANDING_PAGE_PATH } from '../../dashboard_constants'; -import { pluginServices } from '../../services/plugin_services'; +import { coreServices, urlForwardingService } from '../../services/kibana_services'; import { useDashboardMountContext } from '../hooks/dashboard_mount_context'; let bannerId: string | undefined; export const DashboardNoMatch = ({ history }: { history: RouteComponentProps['history'] }) => { const { restorePreviousUrl } = useDashboardMountContext(); - const { - analytics, - settings: { i18n: i18nStart, theme }, - overlays: { banners }, - urlForwarding: { navigateToLegacyKibanaUrl }, - } = pluginServices.getServices(); useEffect(() => { restorePreviousUrl(); - const { navigated } = navigateToLegacyKibanaUrl( + const { navigated } = urlForwardingService.navigateToLegacyKibanaUrl( history.location.pathname + history.location.search ); @@ -41,7 +35,7 @@ export const DashboardNoMatch = ({ history }: { history: RouteComponentProps['hi defaultMessage: 'Page not found', }); - bannerId = banners.replace( + bannerId = coreServices.overlays.banners.replace( bannerId, toMountPoint( @@ -55,28 +49,20 @@ export const DashboardNoMatch = ({ history }: { history: RouteComponentProps['hi />

, - { analytics, i18n: i18nStart, theme } + { analytics: coreServices.analytics, i18n: coreServices.i18n, theme: coreServices.theme } ) ); // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around setTimeout(() => { if (bannerId) { - banners.remove(bannerId); + coreServices.overlays.banners.remove(bannerId); } }, 15000); history.replace(LANDING_PAGE_PATH); } - }, [ - restorePreviousUrl, - navigateToLegacyKibanaUrl, - banners, - analytics, - i18nStart, - theme, - history, - ]); + }, [restorePreviousUrl, history]); return null; }; diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.ts b/src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.ts index 4483634ddda89..aa932ebd8bf3a 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.ts +++ b/src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.ts @@ -8,27 +8,23 @@ */ import type { QueryState } from '@kbn/data-plugin/public'; -import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; -import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; +import { IKbnUrlStateStorage, setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; + import { DASHBOARD_APP_ID, - createDashboardEditUrl, GLOBAL_STATE_STORAGE_KEY, + createDashboardEditUrl, } from '../../dashboard_constants'; -import { pluginServices } from '../../services/plugin_services'; +import { coreServices } from '../../services/kibana_services'; export const getDashboardListItemLink = ( kbnUrlStateStorage: IKbnUrlStateStorage, id: string, timeRestore: boolean ) => { - const { - application: { getUrlForApp }, - settings: { uiSettings }, - } = pluginServices.getServices(); - const useHash = uiSettings.get('state:storeInSessionStorage'); // use hash + const useHash = coreServices.uiSettings.get('state:storeInSessionStorage'); // use hash - let url = getUrlForApp(DASHBOARD_APP_ID, { + let url = coreServices.application.getUrlForApp(DASHBOARD_APP_ID, { path: `#${createDashboardEditUrl(id)}`, }); const globalStateInUrl = kbnUrlStateStorage.get(GLOBAL_STATE_STORAGE_KEY) || {}; diff --git a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx index abf7799369284..15b3d00d07ec7 100644 --- a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx +++ b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx @@ -10,44 +10,30 @@ import React from 'react'; import { withSuspense } from '@kbn/shared-ux-utility'; -import { pluginServices } from '../../services/plugin_services'; + import { DASHBOARD_APP_ID } from '../../dashboard_constants'; +import { + coreServices, + dataService, + dataViewEditorService, + embeddableService, + noDataPageService, + shareService, +} from '../../services/kibana_services'; +import { getDashboardBackupService } from '../../services/dashboard_backup_service'; +import { getDashboardContentManagementService } from '../../services/dashboard_content_management_service'; export const DashboardAppNoDataPage = ({ onDataViewCreated, }: { onDataViewCreated: () => void; }) => { - const { - application, - data: { dataViews }, - dataViewEditor, - http: { basePath, get }, - documentationLinks: { indexPatternsDocLink, kibanaGuideDocLink, esqlDocLink }, - customBranding, - noDataPage, - share, - } = pluginServices.getServices(); - const analyticsServices = { - coreStart: { - docLinks: { - links: { - kibana: { guide: kibanaGuideDocLink }, - indexPatterns: { introduction: indexPatternsDocLink }, - query: { queryESQL: esqlDocLink }, - }, - }, - application, - http: { basePath, get }, - customBranding: { - hasCustomBranding$: customBranding.hasCustomBranding$, - }, - }, - dataViews, - dataViewEditor, - noDataPage, - share: share.url ? { url: share.url } : undefined, + coreStart: coreServices, + dataViews: dataService.dataViews, + dataViewEditor: dataViewEditorService, + noDataPage: noDataPageService, + share: shareService, }; const importPromise = import('@kbn/shared-ux-page-analytics-no-data'); @@ -74,29 +60,22 @@ export const DashboardAppNoDataPage = ({ }; export const isDashboardAppInNoDataState = async () => { - const { - data: { dataViews }, - embeddable, - dashboardContentManagement, - dashboardBackup, - } = pluginServices.getServices(); - - const hasUserDataView = await dataViews.hasData.hasUserDataView().catch(() => false); + const hasUserDataView = await dataService.dataViews.hasData.hasUserDataView().catch(() => false); if (hasUserDataView) return false; // consider has data if there is an incoming embeddable - const hasIncomingEmbeddable = embeddable + const hasIncomingEmbeddable = embeddableService .getStateTransfer() .getIncomingEmbeddablePackage(DASHBOARD_APP_ID, false); if (hasIncomingEmbeddable) return false; // consider has data if there is unsaved dashboard with edits - if (dashboardBackup.dashboardHasUnsavedEdits()) return false; + if (getDashboardBackupService().dashboardHasUnsavedEdits()) return false; // consider has data if there is at least one dashboard - const { total } = await dashboardContentManagement.findDashboards - .search({ search: '', size: 1 }) + const { total } = await getDashboardContentManagementService() + .findDashboards.search({ search: '', size: 1 }) .catch(() => ({ total: 0 })); if (total > 0) return false; diff --git a/src/plugins/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx b/src/plugins/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx index fab0a3cb91c37..e102e6f898c9b 100644 --- a/src/plugins/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx +++ b/src/plugins/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx @@ -10,14 +10,11 @@ import { useEffect } from 'react'; import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; -import { pluginServices } from '../../services/plugin_services'; import { DashboardApi } from '../..'; import { getNewDashboardTitle } from '../_dashboard_app_strings'; +import { coreServices } from '../../services/kibana_services'; export const DashboardTabTitleSetter = ({ dashboardApi }: { dashboardApi: DashboardApi }) => { - const { - chrome: { docTitle: chromeDocTitle }, - } = pluginServices.getServices(); const [title, lastSavedId] = useBatchedPublishingSubjects( dashboardApi.panelTitle, dashboardApi.savedObjectId @@ -27,8 +24,10 @@ export const DashboardTabTitleSetter = ({ dashboardApi }: { dashboardApi: Dashbo * Set chrome tab title when dashboard's title changes */ useEffect(() => { - chromeDocTitle.change(!lastSavedId ? getNewDashboardTitle() : title ?? lastSavedId); - }, [title, chromeDocTitle, lastSavedId]); + coreServices.chrome.docTitle.change( + !lastSavedId ? getNewDashboardTitle() : title ?? lastSavedId + ); + }, [title, lastSavedId]); return null; }; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.test.ts b/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.test.ts index 13ba9e7e37516..7a5a77bfc14fe 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.test.ts +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.test.ts @@ -7,14 +7,20 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { renderHook } from '@testing-library/react-hooks'; -import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; -import type { PresentationContainer } from '@kbn/presentation-containers'; -import type { Action } from '@kbn/ui-actions-plugin/public'; -import { type BaseVisType, VisGroups, VisTypeAlias } from '@kbn/visualizations-plugin/public'; import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; +import type { PresentationContainer } from '@kbn/presentation-containers'; +import type { Action, UiActionsService } from '@kbn/ui-actions-plugin/public'; +import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; +import { + VisGroups, + VisTypeAlias, + VisualizationsStart, + type BaseVisType, +} from '@kbn/visualizations-plugin/public'; +import { renderHook } from '@testing-library/react-hooks'; + +import { uiActionsService, visualizationsService } from '../../../services/kibana_services'; import { useGetDashboardPanels } from './use_get_dashboard_panels'; -import { pluginServices } from '../../../services/plugin_services'; const mockApi = { addNewPanel: jest.fn() } as unknown as jest.Mocked; @@ -24,34 +30,25 @@ describe('Get dashboard panels hook', () => { createNewVisType: jest.fn(), }; - type PluginServices = ReturnType; - let compatibleTriggerActionsRequestSpy: jest.SpyInstance< - ReturnType> + ReturnType> >; let dashboardVisualizationGroupGetterSpy: jest.SpyInstance< - ReturnType + ReturnType >; let dashboardVisualizationAliasesGetterSpy: jest.SpyInstance< - ReturnType + ReturnType >; beforeAll(() => { - const _pluginServices = pluginServices.getServices(); - compatibleTriggerActionsRequestSpy = jest.spyOn( - _pluginServices.uiActions, + uiActionsService, 'getTriggerCompatibleActions' ); - - dashboardVisualizationGroupGetterSpy = jest.spyOn(_pluginServices.visualizations, 'getByGroup'); - - dashboardVisualizationAliasesGetterSpy = jest.spyOn( - _pluginServices.visualizations, - 'getAliases' - ); + dashboardVisualizationGroupGetterSpy = jest.spyOn(visualizationsService, 'getByGroup'); + dashboardVisualizationAliasesGetterSpy = jest.spyOn(visualizationsService, 'getAliases'); }); beforeEach(() => { diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.ts b/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.ts index 493eeb004dd87..d074bcb98bd18 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.ts +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/use_get_dashboard_panels.ts @@ -7,19 +7,20 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { useMemo, useRef, useCallback } from 'react'; +import { useCallback, useMemo, useRef } from 'react'; +import { AsyncSubject, defer, from, lastValueFrom, map, type Subscription } from 'rxjs'; + import type { IconType } from '@elastic/eui'; -import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; -import { type Subscription, AsyncSubject, from, defer, map, lastValueFrom } from 'rxjs'; -import { EmbeddableFactory, COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; +import { COMMON_EMBEDDABLE_GROUPING, EmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { PresentationContainer } from '@kbn/presentation-containers'; -import { type BaseVisType, VisGroups, type VisTypeAlias } from '@kbn/visualizations-plugin/public'; +import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; +import { VisGroups, type BaseVisType, type VisTypeAlias } from '@kbn/visualizations-plugin/public'; -import { pluginServices } from '../../../services/plugin_services'; +import { uiActionsService, visualizationsService } from '../../../services/kibana_services'; import { getAddPanelActionMenuItemsGroup, - type PanelSelectionMenuItem, type GroupedAddPanelActions, + type PanelSelectionMenuItem, } from './add_panel_action_menu_items'; interface UseGetDashboardPanelsArgs { @@ -46,13 +47,9 @@ export const useGetDashboardPanels = ({ api, createNewVisType }: UseGetDashboard const panelsComputeResultCache = useRef(new AsyncSubject()); const panelsComputeSubscription = useRef(null); - const { - uiActions, - visualizations: { getAliases: getVisTypeAliases, getByGroup: getVisTypesByGroup }, - } = pluginServices.getServices(); - const getSortedVisTypesByGroup = (group: VisGroups) => - getVisTypesByGroup(group) + visualizationsService + .getByGroup(group) .sort((a: BaseVisType | VisTypeAlias, b: BaseVisType | VisTypeAlias) => { const labelA = 'titleInWizard' in a ? a.titleInWizard || a.title : a.title; const labelB = 'titleInWizard' in b ? b.titleInWizard || a.title : a.title; @@ -70,7 +67,8 @@ export const useGetDashboardPanels = ({ api, createNewVisType }: UseGetDashboard const toolVisTypes = getSortedVisTypesByGroup(VisGroups.TOOLS); const legacyVisTypes = getSortedVisTypesByGroup(VisGroups.LEGACY); - const visTypeAliases = getVisTypeAliases() + const visTypeAliases = visualizationsService + .getAliases() .sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => a === b ? 0 : a ? -1 : 1 ) @@ -133,12 +131,12 @@ export const useGetDashboardPanels = ({ api, createNewVisType }: UseGetDashboard () => defer(() => { return from( - uiActions?.getTriggerCompatibleActions?.(ADD_PANEL_TRIGGER, { + uiActionsService.getTriggerCompatibleActions?.(ADD_PANEL_TRIGGER, { embeddable: api, }) ?? [] ); }), - [api, uiActions] + [api] ); const computeAvailablePanels = useCallback( diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx index ae91af4891bf2..ddc629854affe 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx @@ -7,40 +7,35 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; -import React, { useCallback } from 'react'; import { METRIC_TYPE } from '@kbn/analytics'; -import { useEuiTheme } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; import { AddFromLibraryButton, Toolbar, ToolbarButton } from '@kbn/shared-ux-button-toolbar'; import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; import { useStateFromPublishingSubject } from '@kbn/presentation-publishing'; +import { useDashboardApi } from '../../dashboard_api/use_dashboard_api'; +import { DASHBOARD_UI_METRIC_ID } from '../../dashboard_constants'; +import { + dataService, + embeddableService, + usageCollectionService, + visualizationsService, +} from '../../services/kibana_services'; import { getCreateVisualizationButtonTitle } from '../_dashboard_app_strings'; -import { EditorMenu } from './editor_menu'; -import { pluginServices } from '../../services/plugin_services'; import { ControlsToolbarButton } from './controls_toolbar_button'; -import { DASHBOARD_UI_METRIC_ID } from '../../dashboard_constants'; -import { useDashboardApi } from '../../dashboard_api/use_dashboard_api'; +import { EditorMenu } from './editor_menu'; export function DashboardEditingToolbar({ isDisabled }: { isDisabled?: boolean }) { - const { - usageCollection, - data: { search }, - embeddable: { getStateTransfer }, - visualizations: { getAliases: getVisTypeAliases }, - } = pluginServices.getServices(); const { euiTheme } = useEuiTheme(); const dashboardApi = useDashboardApi(); - const stateTransferService = getStateTransfer(); - - const lensAlias = getVisTypeAliases().find(({ name }) => name === 'lens'); - - const trackUiMetric = usageCollection.reportUiCounter?.bind( - usageCollection, - DASHBOARD_UI_METRIC_ID + const lensAlias = useMemo( + () => visualizationsService.getAliases().find(({ name }) => name === 'lens'), + [] ); const createNewVisType = useCallback( @@ -49,6 +44,10 @@ export function DashboardEditingToolbar({ isDisabled }: { isDisabled?: boolean } let appId = ''; if (visType) { + const trackUiMetric = usageCollectionService?.reportUiCounter.bind( + usageCollectionService, + DASHBOARD_UI_METRIC_ID + ); if (trackUiMetric) { trackUiMetric(METRIC_TYPE.CLICK, `${visType.name}:create`); } @@ -67,16 +66,17 @@ export function DashboardEditingToolbar({ isDisabled }: { isDisabled?: boolean } path = '#/create?'; } + const stateTransferService = embeddableService.getStateTransfer(); stateTransferService.navigateToEditor(appId, { path, state: { originatingApp: dashboardApi.getAppContext()?.currentAppId, originatingPath: dashboardApi.getAppContext()?.getCurrentPath?.(), - searchSessionId: search.session.getSessionId(), + searchSessionId: dataService.search.session.getSessionId(), }, }); }, - [stateTransferService, dashboardApi, search.session, trackUiMetric] + [dashboardApi] ); /** diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx index a867d0133b46d..7850c1e9ed745 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx @@ -7,35 +7,23 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; import { render } from '@testing-library/react'; -import { EditorMenu } from './editor_menu'; +import React from 'react'; import { buildMockDashboard } from '../../mocks'; +import { EditorMenu } from './editor_menu'; -import { pluginServices } from '../../services/plugin_services'; -import { DashboardContext } from '../../dashboard_api/use_dashboard_api'; import { DashboardApi } from '../../dashboard_api/types'; +import { DashboardContext } from '../../dashboard_api/use_dashboard_api'; +import { + embeddableService, + uiActionsService, + visualizationsService, +} from '../../services/kibana_services'; -jest.mock('../../services/plugin_services', () => { - const module = jest.requireActual('../../services/plugin_services'); - - const _pluginServices = (module.pluginServices as typeof pluginServices).getServices(); - - jest - .spyOn(_pluginServices.embeddable, 'getEmbeddableFactories') - .mockReturnValue(new Map().values()); - jest.spyOn(_pluginServices.uiActions, 'getTriggerCompatibleActions').mockResolvedValue([]); - jest.spyOn(_pluginServices.visualizations, 'getByGroup').mockReturnValue([]); - jest.spyOn(_pluginServices.visualizations, 'getAliases').mockReturnValue([]); - - return { - ...module, - pluginServices: { - ...module.pluginServices, - getServices: jest.fn().mockReturnValue(_pluginServices), - }, - }; -}); +jest.spyOn(embeddableService, 'getEmbeddableFactories').mockReturnValue(new Map().values()); +jest.spyOn(uiActionsService, 'getTriggerCompatibleActions').mockResolvedValue([]); +jest.spyOn(visualizationsService, 'getByGroup').mockReturnValue([]); +jest.spyOn(visualizationsService, 'getAliases').mockReturnValue([]); describe('editor menu', () => { it('renders without crashing', async () => { diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx index a79024fe8b9dc..2cad63c442026 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx @@ -16,8 +16,8 @@ import { toMountPoint } from '@kbn/react-kibana-mount'; import { ToolbarButton } from '@kbn/shared-ux-button-toolbar'; import { useGetDashboardPanels, DashboardPanelSelectionListFlyout } from './add_new_panel'; -import { pluginServices } from '../../services/plugin_services'; import { useDashboardApi } from '../../dashboard_api/use_dashboard_api'; +import { coreServices } from '../../services/kibana_services'; interface EditorMenuProps extends Pick[0], 'createNewVisType'> { @@ -27,12 +27,6 @@ interface EditorMenuProps export const EditorMenu = ({ createNewVisType, isDisabled }: EditorMenuProps) => { const dashboardApi = useDashboardApi(); - const { - overlays, - analytics, - settings: { i18n: i18nStart, theme }, - } = pluginServices.getServices(); - const fetchDashboardPanels = useGetDashboardPanels({ api: dashboardApi, createNewVisType, @@ -63,11 +57,11 @@ export const EditorMenu = ({ createNewVisType, isDisabled }: EditorMenuProps) => /> ); }), - { analytics, theme, i18n: i18nStart } + { analytics: coreServices.analytics, theme: coreServices.theme, i18n: coreServices.i18n } ); dashboardApi.openOverlay( - overlays.openFlyout(mount, { + coreServices.overlays.openFlyout(mount, { size: 'm', maxWidth: 500, paddingSize: flyoutPanelPaddingSize, @@ -80,7 +74,7 @@ export const EditorMenu = ({ createNewVisType, isDisabled }: EditorMenuProps) => }) ); }, - [analytics, theme, i18nStart, dashboardApi, overlays, fetchDashboardPanels] + [dashboardApi, fetchDashboardPanels] ); return ( diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx index ff91eaf896481..41c4a55f6ab8d 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx @@ -8,11 +8,12 @@ */ import { Capabilities } from '@kbn/core/public'; -import { DashboardLocatorParams } from '../../../dashboard_container'; import { convertPanelMapToSavedPanels, DashboardContainerInput } from '../../../../common'; +import { DashboardLocatorParams } from '../../../dashboard_container'; -import { pluginServices } from '../../../services/plugin_services'; +import { shareService } from '../../../services/kibana_services'; import { showPublicUrlSwitch, ShowShareModal, ShowShareModalProps } from './show_share_modal'; +import { getDashboardBackupService } from '../../../services/dashboard_backup_service'; describe('showPublicUrlSwitch', () => { test('returns false if "dashboard" app is not available', () => { @@ -56,13 +57,11 @@ describe('showPublicUrlSwitch', () => { }); describe('ShowShareModal', () => { + const dashboardBackupService = getDashboardBackupService(); const unsavedStateKeys = ['query', 'filters', 'options', 'savedQuery', 'panels'] as Array< keyof DashboardLocatorParams >; - const toggleShareMenuSpy = jest.spyOn( - pluginServices.getServices().share, - 'toggleShareContextMenu' - ); + const toggleShareMenuSpy = jest.spyOn(shareService!, 'toggleShareContextMenu'); afterEach(() => { jest.clearAllMocks(); @@ -71,9 +70,7 @@ describe('ShowShareModal', () => { const getPropsAndShare = ( unsavedState?: Partial ): ShowShareModalProps => { - pluginServices.getServices().dashboardBackup.getState = jest - .fn() - .mockReturnValue({ dashboardState: unsavedState }); + dashboardBackupService.getState = jest.fn().mockReturnValue({ dashboardState: unsavedState }); return { isDirty: true, anchorElement: document.createElement('div'), @@ -169,7 +166,7 @@ describe('ShowShareModal', () => { }, }; const props = getPropsAndShare(unsavedDashboardState); - pluginServices.getServices().dashboardBackup.getState = jest.fn().mockReturnValue({ + dashboardBackupService.getState = jest.fn().mockReturnValue({ dashboardState: unsavedDashboardState, panels: { panel_1: { changedKey1: 'changed' }, diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx index 81226fbb5b298..5dd56465de920 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx @@ -7,6 +7,10 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { omit } from 'lodash'; +import moment from 'moment'; +import React, { ReactElement, useState } from 'react'; + import { EuiCheckboxGroup } from '@elastic/eui'; import type { Capabilities } from '@kbn/core/public'; import { QueryState } from '@kbn/data-plugin/common'; @@ -14,15 +18,17 @@ import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; import { getStateFromKbnUrl, setStateToKbnUrl, unhashUrl } from '@kbn/kibana-utils-plugin/public'; -import { omit } from 'lodash'; -import moment from 'moment'; -import React, { ReactElement, useState } from 'react'; + import { convertPanelMapToSavedPanels, DashboardPanelMap } from '../../../../common'; import { DashboardLocatorParams } from '../../../dashboard_container'; -import { pluginServices } from '../../../services/plugin_services'; -import { dashboardUrlParams } from '../../dashboard_router'; +import { + getDashboardBackupService, + PANELS_CONTROL_GROUP_KEY, +} from '../../../services/dashboard_backup_service'; +import { coreServices, dataService, shareService } from '../../../services/kibana_services'; +import { getDashboardCapabilities } from '../../../utils/get_dashboard_capabilities'; import { shareModalStrings } from '../../_dashboard_app_strings'; -import { PANELS_CONTROL_GROUP_KEY } from '../../../services/dashboard_backup/dashboard_backup_service'; +import { dashboardUrlParams } from '../../dashboard_router'; const showFilterBarId = 'showFilterBar'; @@ -49,21 +55,7 @@ export function ShowShareModal({ dashboardTitle, getPanelsState, }: ShowShareModalProps) { - const { - dashboardCapabilities: { createShortUrl: allowShortUrl }, - dashboardBackup, - data: { - query: { - timefilter: { - timefilter: { getTime }, - }, - }, - }, - notifications, - share: { toggleShareContextMenu }, - } = pluginServices.getServices(); - - if (!toggleShareContextMenu) return; // TODO: Make this logic cleaner once share is an optional service + if (!shareService) return; const EmbedUrlParamExtension = ({ setParamValue, @@ -125,7 +117,7 @@ export function ShowShareModal({ let unsavedStateForLocator: DashboardLocatorParams = {}; const { dashboardState: unsavedDashboardState, panels: panelModifications } = - dashboardBackup.getState(savedObjectId) ?? {}; + getDashboardBackupService().getState(savedObjectId) ?? {}; const allUnsavedPanels = (() => { if ( @@ -186,7 +178,7 @@ export function ShowShareModal({ refreshInterval: undefined, // We don't share refresh interval externally viewMode: ViewMode.VIEW, // For share locators we always load the dashboard in view mode useHash: false, - timeRange: getTime(), + timeRange: dataService.query.timefilter.timefilter.getTime(), ...unsavedStateForLocator, }; @@ -203,11 +195,11 @@ export function ShowShareModal({ unhashUrl(baseUrl) ); - toggleShareContextMenu({ + shareService.toggleShareContextMenu({ isDirty, anchorElement, allowEmbed: true, - allowShortUrl, + allowShortUrl: getDashboardCapabilities().createShortUrl, shareableUrl, objectId: savedObjectId, objectType: 'dashboard', @@ -238,6 +230,6 @@ export function ShowShareModal({ snapshotShareWarning: Boolean(unsavedDashboardState?.panels) ? shareModalStrings.getSnapshotShareWarning() : undefined, - toasts: notifications.toasts, + toasts: coreServices.notifications.toasts, }); } diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx index 41ddee24a9330..ca58d3c74bd3f 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx @@ -7,23 +7,25 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { batch } from 'react-redux'; import { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react'; +import { batch } from 'react-redux'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { TopNavMenuData } from '@kbn/navigation-plugin/public'; +import type { TopNavMenuData } from '@kbn/navigation-plugin/public'; import useMountedState from 'react-use/lib/useMountedState'; import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { UI_SETTINGS } from '../../../common'; -import { topNavStrings } from '../_dashboard_app_strings'; -import { ShowShareModal } from './share/show_share_modal'; -import { pluginServices } from '../../services/plugin_services'; -import { CHANGE_CHECK_DEBOUNCE } from '../../dashboard_constants'; -import { confirmDiscardUnsavedChanges } from '../../dashboard_listing/confirm_overlays'; -import { SaveDashboardReturn } from '../../services/dashboard_content_management/types'; import { useDashboardApi } from '../../dashboard_api/use_dashboard_api'; +import { CHANGE_CHECK_DEBOUNCE } from '../../dashboard_constants'; import { openSettingsFlyout } from '../../dashboard_container/embeddable/api'; +import { confirmDiscardUnsavedChanges } from '../../dashboard_listing/confirm_overlays'; +import { getDashboardBackupService } from '../../services/dashboard_backup_service'; +import { SaveDashboardReturn } from '../../services/dashboard_content_management_service/types'; +import { coreServices, shareService } from '../../services/kibana_services'; +import { getDashboardCapabilities } from '../../utils/get_dashboard_capabilities'; +import { topNavStrings } from '../_dashboard_app_strings'; +import { ShowShareModal } from './share/show_share_modal'; export const useDashboardMenuItems = ({ isLabsShown, @@ -40,17 +42,6 @@ export const useDashboardMenuItems = ({ const [isSaveInProgress, setIsSaveInProgress] = useState(false); - /** - * Unpack dashboard services - */ - const { - share, - dashboardBackup, - settings: { uiSettings }, - dashboardCapabilities: { showWriteControls }, - } = pluginServices.getServices(); - const isLabsEnabled = uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI); - /** * Unpack dashboard state from redux */ @@ -120,7 +111,7 @@ export const useDashboardMenuItems = ({ const switchModes = switchToViewMode ? () => { dashboardApi.setViewMode(ViewMode.VIEW); - dashboardBackup.storeViewMode(ViewMode.VIEW); + getDashboardBackupService().storeViewMode(ViewMode.VIEW); } : undefined; if (!hasUnsavedChanges) { @@ -138,7 +129,7 @@ export const useDashboardMenuItems = ({ }); }, viewMode as ViewMode); }, - [dashboardApi, dashboardBackup, hasUnsavedChanges, viewMode, isMounted] + [dashboardApi, hasUnsavedChanges, viewMode, isMounted] ); /** @@ -170,7 +161,7 @@ export const useDashboardMenuItems = ({ testId: 'dashboardEditMode', className: 'eui-hideFor--s eui-hideFor--xs', // hide for small screens - editing doesn't work in mobile mode. run: () => { - dashboardBackup.storeViewMode(ViewMode.EDIT); + getDashboardBackupService().storeViewMode(ViewMode.EDIT); dashboardApi.setViewMode(ViewMode.EDIT); dashboardApi.clearOverlays(); }, @@ -243,7 +234,6 @@ export const useDashboardMenuItems = ({ dashboardApi, setIsLabsShown, isLabsShown, - dashboardBackup, quickSaveDashboard, resetChanges, isResetting, @@ -275,9 +265,13 @@ export const useDashboardMenuItems = ({ /** * Build ordered menus for view and edit mode. */ + const isLabsEnabled = useMemo(() => coreServices.uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI), []); + const viewModeTopNavConfig = useMemo(() => { + const { showWriteControls } = getDashboardCapabilities(); + const labsMenuItem = isLabsEnabled ? [menuItems.labs] : []; - const shareMenuItem = share ? [menuItems.share] : []; + const shareMenuItem = shareService ? [menuItems.share] : []; const duplicateMenuItem = showWriteControls ? [menuItems.interactiveSave] : []; const editMenuItem = showWriteControls && !managed ? [menuItems.edit] : []; const mayberesetChangesMenuItem = showResetChange ? [resetChangesMenuItem] : []; @@ -290,19 +284,11 @@ export const useDashboardMenuItems = ({ ...mayberesetChangesMenuItem, ...editMenuItem, ]; - }, [ - isLabsEnabled, - menuItems, - share, - showWriteControls, - managed, - showResetChange, - resetChangesMenuItem, - ]); + }, [isLabsEnabled, menuItems, managed, showResetChange, resetChangesMenuItem]); const editModeTopNavConfig = useMemo(() => { const labsMenuItem = isLabsEnabled ? [menuItems.labs] : []; - const shareMenuItem = share ? [menuItems.share] : []; + const shareMenuItem = shareService ? [menuItems.share] : []; const editModeItems: TopNavMenuData[] = []; if (lastSavedId) { @@ -317,7 +303,7 @@ export const useDashboardMenuItems = ({ editModeItems.push(menuItems.switchToViewMode, menuItems.interactiveSave); } return [...labsMenuItem, menuItems.settings, ...shareMenuItem, ...editModeItems]; - }, [isLabsEnabled, menuItems, share, lastSavedId, showResetChange, resetChangesMenuItem]); + }, [isLabsEnabled, menuItems, lastSavedId, showResetChange, resetChangesMenuItem]); return { viewModeTopNavConfig, editModeTopNavConfig }; }; diff --git a/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts b/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts index 8092f99a0bb3a..5ec8a0716d410 100644 --- a/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts +++ b/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts @@ -23,7 +23,7 @@ import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; import { SEARCH_SESSION_ID } from '../../dashboard_constants'; import { DashboardContainer, DashboardLocatorParams } from '../../dashboard_container'; import { convertPanelMapToSavedPanels } from '../../../common'; -import { pluginServices } from '../../services/plugin_services'; +import { dataService } from '../../services/kibana_services'; export const removeSearchSessionIdFromURL = (kbnUrlStateStorage: IKbnUrlStateStorage) => { kbnUrlStateStorage.kbnUrlControls.updateAsync((nextUrl) => { @@ -69,17 +69,6 @@ function getLocatorParams({ container: DashboardContainer; shouldRestoreSearchSession: boolean; }): DashboardLocatorParams { - const { - data: { - query: { - queryString, - filterManager, - timefilter: { timefilter }, - }, - search: { session }, - }, - } = pluginServices.getServices(); - const { componentState: { lastSavedId }, explicitInput: { panels, query, viewMode }, @@ -89,11 +78,15 @@ function getLocatorParams({ viewMode, useHash: false, preserveSavedFilters: false, - filters: filterManager.getFilters(), - query: queryString.formatQuery(query) as Query, + filters: dataService.query.filterManager.getFilters(), + query: dataService.query.queryString.formatQuery(query) as Query, dashboardId: container.getDashboardSavedObjectId(), - searchSessionId: shouldRestoreSearchSession ? session.getSessionId() : undefined, - timeRange: shouldRestoreSearchSession ? timefilter.getAbsoluteTime() : timefilter.getTime(), + searchSessionId: shouldRestoreSearchSession + ? dataService.search.session.getSessionId() + : undefined, + timeRange: shouldRestoreSearchSession + ? dataService.query.timefilter.timefilter.getAbsoluteTime() + : dataService.query.timefilter.timefilter.getTime(), refreshInterval: shouldRestoreSearchSession ? { pause: true, // force pause refresh interval when restoring a session diff --git a/src/plugins/dashboard/public/dashboard_app/url/url_utils.ts b/src/plugins/dashboard/public/dashboard_app/url/url_utils.ts index 09e15abe5eea1..b748909eac9ac 100644 --- a/src/plugins/dashboard/public/dashboard_app/url/url_utils.ts +++ b/src/plugins/dashboard/public/dashboard_app/url/url_utils.ts @@ -7,26 +7,26 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { History } from 'history'; import _ from 'lodash'; import { skip } from 'rxjs'; import semverSatisfies from 'semver/functions/satisfies'; -import { History } from 'history'; -import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/common'; +import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { + DashboardContainerInput, DashboardPanelMap, SharedDashboardState, convertSavedPanelsToPanelMap, - DashboardContainerInput, } from '../../../common'; -import { pluginServices } from '../../services/plugin_services'; -import { getPanelTooOldErrorString } from '../_dashboard_app_strings'; -import { DASHBOARD_STATE_STORAGE_KEY, createDashboardEditUrl } from '../../dashboard_constants'; import { SavedDashboardPanel } from '../../../common/content_management'; -import { migrateLegacyQuery } from '../../services/dashboard_content_management/lib/load_dashboard_state'; import { DashboardApi } from '../../dashboard_api/types'; +import { DASHBOARD_STATE_STORAGE_KEY, createDashboardEditUrl } from '../../dashboard_constants'; +import { migrateLegacyQuery } from '../../services/dashboard_content_management_service/lib/load_dashboard_state'; +import { coreServices } from '../../services/kibana_services'; +import { getPanelTooOldErrorString } from '../_dashboard_app_strings'; /** * We no longer support loading panels from a version older than 7.3 in the URL. @@ -54,7 +54,7 @@ function getPanelsMap(appStateInUrl: SharedDashboardState): DashboardPanelMap | } if (isPanelVersionTooOld(appStateInUrl.panels)) { - pluginServices.getServices().notifications.toasts.addWarning(getPanelTooOldErrorString()); + coreServices.notifications.toasts.addWarning(getPanelTooOldErrorString()); return undefined; } diff --git a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx index 4204953e22696..b167f68e1595b 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx @@ -7,20 +7,18 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import React from 'react'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { DashboardApi } from '../../../dashboard_api/types'; +import { DashboardContext } from '../../../dashboard_api/use_dashboard_api'; import { buildMockDashboard } from '../../../mocks'; +import { coreServices, visualizationsService } from '../../../services/kibana_services'; import { DashboardEmptyScreen } from './dashboard_empty_screen'; -import { pluginServices } from '../../../services/plugin_services'; -import { DashboardContext } from '../../../dashboard_api/use_dashboard_api'; -import { DashboardApi } from '../../../dashboard_api/types'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; -pluginServices.getServices().visualizations.getAliases = jest - .fn() - .mockReturnValue([{ name: 'lens' }]); +visualizationsService.getAliases = jest.fn().mockReturnValue([{ name: 'lens' }]); describe('DashboardEmptyScreen', () => { function mountComponent(viewMode: ViewMode) { @@ -57,7 +55,7 @@ describe('DashboardEmptyScreen', () => { }); test('renders correctly with readonly mode', () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + (coreServices.application.capabilities as any).dashboard.showWriteControls = false; const component = mountComponent(ViewMode.VIEW); expect(component.render()).toMatchSnapshot(); @@ -72,7 +70,7 @@ describe('DashboardEmptyScreen', () => { // even when in edit mode, readonly users should not have access to the editing buttons in the empty prompt. test('renders correctly with readonly and edit mode', () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + (coreServices.application.capabilities as any).dashboard.showWriteControls = false; const component = mountComponent(ViewMode.EDIT); expect(component.render()).toMatchSnapshot(); diff --git a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx index 91edfcc5fc3b2..e4e82339e7c22 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx @@ -21,33 +21,31 @@ import { } from '@elastic/eui'; import { METRIC_TYPE } from '@kbn/analytics'; import { ViewMode } from '@kbn/embeddable-plugin/public'; - import { useStateFromPublishingSubject } from '@kbn/presentation-publishing'; -import { DASHBOARD_UI_METRIC_ID } from '../../../dashboard_constants'; -import { pluginServices } from '../../../services/plugin_services'; + import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api'; +import { DASHBOARD_UI_METRIC_ID } from '../../../dashboard_constants'; +import { + coreServices, + dataService, + embeddableService, + usageCollectionService, + visualizationsService, +} from '../../../services/kibana_services'; +import { getDashboardCapabilities } from '../../../utils/get_dashboard_capabilities'; import { emptyScreenStrings } from '../../_dashboard_container_strings'; export function DashboardEmptyScreen() { - const { - settings: { - theme: { theme$ }, - }, - usageCollection, - data: { search }, - http: { basePath }, - embeddable: { getStateTransfer }, - dashboardCapabilities: { showWriteControls }, - visualizations: { getAliases: getVisTypeAliases }, - } = pluginServices.getServices(); - const lensAlias = useMemo( - () => getVisTypeAliases().find(({ name }) => name === 'lens'), - [getVisTypeAliases] + () => visualizationsService.getAliases().find(({ name }) => name === 'lens'), + [] ); + const { showWriteControls } = useMemo(() => { + return getDashboardCapabilities(); + }, []); const dashboardApi = useDashboardApi(); - const isDarkTheme = useObservable(theme$)?.darkMode; + const isDarkTheme = useObservable(coreServices.theme.theme$)?.darkMode; const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode); const isEditMode = useMemo(() => { return viewMode === 'edit'; @@ -55,8 +53,8 @@ export function DashboardEmptyScreen() { const goToLens = useCallback(() => { if (!lensAlias || !lensAlias.alias) return; - const trackUiMetric = usageCollection.reportUiCounter?.bind( - usageCollection, + const trackUiMetric = usageCollectionService?.reportUiCounter.bind( + usageCollectionService, DASHBOARD_UI_METRIC_ID ); @@ -64,18 +62,18 @@ export function DashboardEmptyScreen() { trackUiMetric(METRIC_TYPE.CLICK, `${lensAlias.name}:create`); } const appContext = dashboardApi.getAppContext(); - getStateTransfer().navigateToEditor(lensAlias.alias.app, { + embeddableService.getStateTransfer().navigateToEditor(lensAlias.alias.app, { path: lensAlias.alias.path, state: { originatingApp: appContext?.currentAppId, originatingPath: appContext?.getCurrentPath?.() ?? '', - searchSessionId: search.session.getSessionId(), + searchSessionId: dataService.search.session.getSessionId(), }, }); - }, [getStateTransfer, lensAlias, dashboardApi, search.session, usageCollection]); + }, [lensAlias, dashboardApi]); // TODO replace these SVGs with versions from EuiIllustration as soon as it becomes available. - const imageUrl = basePath.prepend( + const imageUrl = coreServices.http.basePath.prepend( `/plugins/dashboard/assets/${isDarkTheme ? 'dashboards_dark' : 'dashboards_light'}.svg` ); diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx index 0a116eae997d4..518b009571e27 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx @@ -7,15 +7,17 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import classNames from 'classnames'; +import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; + import { EuiLoadingChart } from '@elastic/eui'; import { css } from '@emotion/react'; import { EmbeddablePanel, ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public'; -import classNames from 'classnames'; -import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; + import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { DashboardPanelState } from '../../../../common'; -import { pluginServices } from '../../../services/plugin_services'; import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api'; +import { embeddableService, presentationUtilService } from '../../../services/kibana_services'; type DivProps = Pick, 'className' | 'style' | 'children'>; @@ -97,10 +99,6 @@ export const Item = React.forwardRef( : undefined; const renderedEmbeddable = useMemo(() => { - const { - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); - const panelProps = { showBadges: true, showBorder: useMargins, @@ -109,7 +107,7 @@ export const Item = React.forwardRef( }; // render React embeddable - if (reactEmbeddableRegistryHasKey(type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(type)) { return ( ((props, pane // ReactGridLayout passes ref to children. Functional component children require forwardRef to avoid react warning // https://github.com/react-grid-layout/react-grid-layout#custom-child-components-and-draggable-handles export const DashboardGridItem = React.forwardRef((props, ref) => { - const { - settings: { isProjectEnabledInLabs }, - } = pluginServices.getServices(); const dashboardApi = useDashboardApi(); const [focusedPanelId, viewMode] = useBatchedPublishingSubjects( dashboardApi.focusedPanelId$, dashboardApi.viewMode ); + const deferBelowFoldEnabled = useMemo( + () => presentationUtilService.labsService.isProjectEnabled('labs:dashboard:deferBelowFold'), + [] + ); + const isEnabled = viewMode !== 'print' && - isProjectEnabledInLabs('labs:dashboard:deferBelowFold') && + deferBelowFoldEnabled && (!focusedPanelId || focusedPanelId === props.id); return isEnabled ? : ; diff --git a/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx b/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx index d4160d7002949..20fd2b93119de 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx @@ -9,29 +9,32 @@ import React, { useCallback, useState } from 'react'; import useMountedState from 'react-use/lib/useMountedState'; -import { i18n } from '@kbn/i18n'; + import { - EuiFormRow, - EuiFieldText, - EuiTextArea, - EuiForm, EuiButton, EuiButtonEmpty, + EuiCallOut, + EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFlyoutBody, EuiFlyoutFooter, EuiFlyoutHeader, - EuiTitle, - EuiCallOut, + EuiForm, + EuiFormRow, + EuiIconTip, EuiSwitch, EuiText, - EuiIconTip, + EuiTextArea, + EuiTitle, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; + import { DashboardContainerInput } from '../../../../common'; -import { pluginServices } from '../../../services/plugin_services'; import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api'; +import { getDashboardContentManagementService } from '../../../services/dashboard_content_management_service'; +import { savedObjectsTaggingService } from '../../../services/kibana_services'; interface DashboardSettingsProps { onClose: () => void; @@ -40,11 +43,6 @@ interface DashboardSettingsProps { const DUPLICATE_TITLE_CALLOUT_ID = 'duplicateTitleCallout'; export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => { - const { - savedObjectsTagging: { components }, - dashboardContentManagement: { checkForDuplicateDashboardTitle }, - } = pluginServices.getServices(); - const dashboardApi = useDashboardApi(); const [localSettings, setLocalSettings] = useState(dashboardApi.getSettings()); @@ -63,13 +61,15 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => { const onApply = async () => { setIsApplying(true); - const validTitle = await checkForDuplicateDashboardTitle({ - title: localSettings.title, - copyOnSave: false, - lastSavedTitle: dashboardApi.panelTitle.value ?? '', - onTitleDuplicate, - isTitleDuplicateConfirmed, - }); + const validTitle = await getDashboardContentManagementService().checkForDuplicateDashboardTitle( + { + title: localSettings.title, + copyOnSave: false, + lastSavedTitle: dashboardApi.panelTitle.value ?? '', + onTitleDuplicate, + isTitleDuplicateConfirmed, + } + ); if (!isMounted()) return; @@ -121,7 +121,9 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => { }; const renderTagSelector = () => { - if (!components) return; + const savedObjectsTaggingApi = savedObjectsTaggingService?.getTaggingApi(); + if (!savedObjectsTaggingApi) return; + return ( { /> } > - updateDashboardSetting({ tags: selectedTags })} /> diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.test.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.test.tsx index 72dfc8659ae24..47732d52ad40c 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.test.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.test.tsx @@ -15,11 +15,11 @@ import { ReferenceOrValueEmbeddable, } from '@kbn/embeddable-plugin/public'; import { + CONTACT_CARD_EMBEDDABLE, ContactCardEmbeddable, ContactCardEmbeddableFactory, ContactCardEmbeddableInput, ContactCardEmbeddableOutput, - CONTACT_CARD_EMBEDDABLE, } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; import { @@ -34,7 +34,7 @@ import { render } from '@testing-library/react'; import React from 'react'; import { BehaviorSubject, lastValueFrom, Subject } from 'rxjs'; import { buildMockDashboard, getSampleDashboardPanel } from '../../../mocks'; -import { pluginServices } from '../../../services/plugin_services'; +import { embeddableService } from '../../../services/kibana_services'; import { DashboardContainer } from '../dashboard_container'; import { duplicateDashboardPanel, incrementPanelTitle } from './duplicate_dashboard_panel'; @@ -54,9 +54,7 @@ describe('Legacy embeddables', () => { const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockEmbeddableFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockEmbeddableFactory); container = buildMockDashboard({ overrides: { panels: { diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts index 76a600a10decd..d62bb78b3b645 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/duplicate_dashboard_panel.ts @@ -7,6 +7,9 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { filter, map, max } from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; + import { isReferenceOrValueEmbeddable, PanelNotFoundError } from '@kbn/embeddable-plugin/public'; import { apiHasSnapshottableState } from '@kbn/presentation-containers/interfaces/serialized_state'; import { @@ -16,11 +19,10 @@ import { getPanelTitle, stateHasTitles, } from '@kbn/presentation-publishing'; -import { filter, map, max } from 'lodash'; -import { v4 as uuidv4 } from 'uuid'; + import { DashboardPanelState, prefixReferencesFromPanel } from '../../../../common'; import { dashboardClonePanelActionStrings } from '../../../dashboard_actions/_dashboard_actions_strings'; -import { pluginServices } from '../../../services/plugin_services'; +import { coreServices, embeddableService } from '../../../services/kibana_services'; import { placeClonePanel } from '../../panel_placement'; import { DashboardContainer } from '../dashboard_container'; @@ -105,17 +107,13 @@ const duplicateReactEmbeddableInput = async ( }; export async function duplicateDashboardPanel(this: DashboardContainer, idToDuplicate: string) { - const { - notifications: { toasts }, - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); const panelToClone = await this.getDashboardPanelFromId(idToDuplicate); - const duplicatedPanelState = reactEmbeddableRegistryHasKey(panelToClone.type) + const duplicatedPanelState = embeddableService.reactEmbeddableRegistryHasKey(panelToClone.type) ? await duplicateReactEmbeddableInput(this, panelToClone, idToDuplicate) : await duplicateLegacyInput(this, panelToClone, idToDuplicate); - toasts.addSuccess({ + coreServices.notifications.toasts.addSuccess({ title: dashboardClonePanelActionStrings.getSuccessMessage(), 'data-test-subj': 'addObjectToContainerSuccess', }); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/open_settings_flyout.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/open_settings_flyout.tsx index 70955ea67d787..867e6ae9d0477 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/open_settings_flyout.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/open_settings_flyout.tsx @@ -11,20 +11,14 @@ import React from 'react'; import { toMountPoint } from '@kbn/react-kibana-mount'; -import { pluginServices } from '../../../services/plugin_services'; -import { DashboardSettings } from '../../component/settings/settings_flyout'; -import { DashboardContext } from '../../../dashboard_api/use_dashboard_api'; import { DashboardApi } from '../../../dashboard_api/types'; +import { DashboardContext } from '../../../dashboard_api/use_dashboard_api'; +import { coreServices } from '../../../services/kibana_services'; +import { DashboardSettings } from '../../component/settings/settings_flyout'; export function openSettingsFlyout(dashboardApi: DashboardApi) { - const { - analytics, - settings: { i18n, theme }, - overlays, - } = pluginServices.getServices(); - dashboardApi.openOverlay( - overlays.openFlyout( + coreServices.overlays.openFlyout( toMountPoint( , - { analytics, i18n, theme } + { analytics: coreServices.analytics, i18n: coreServices.i18n, theme: coreServices.theme } ), { size: 's', diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/save_modal.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/save_modal.tsx index 318dcb71f7f99..681be0ac9bef8 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/save_modal.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/save_modal.tsx @@ -7,14 +7,15 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { i18n } from '@kbn/i18n'; import React, { Fragment, useCallback } from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiIconTip, EuiSwitch } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFormRow, EuiSwitch, EuiIconTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { SavedObjectSaveModal } from '@kbn/saved-objects-plugin/public'; +import { savedObjectsTaggingService } from '../../../../services/kibana_services'; import type { DashboardSaveOptions } from '../../../types'; -import { pluginServices } from '../../../../services/plugin_services'; /** * TODO: Portable Dashboard followup, use redux for the state. @@ -79,12 +80,9 @@ export const DashboardSaveModal: React.FC = ({ ); const renderDashboardSaveOptions = useCallback(() => { - const { - savedObjectsTagging: { components }, - } = pluginServices.getServices(); - - const tagSelector = components ? ( - { setSelectedTags(selectedTagIds); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx index 244638fffe90e..7230ff4fcb619 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx @@ -7,6 +7,10 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { cloneDeep } from 'lodash'; +import React from 'react'; +import { batch } from 'react-redux'; + import type { Reference } from '@kbn/content-management-utils'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { @@ -14,12 +18,10 @@ import { isReferenceOrValueEmbeddable, ViewMode, } from '@kbn/embeddable-plugin/public'; +import { i18n } from '@kbn/i18n'; import { apiHasSerializableState, SerializedPanelState } from '@kbn/presentation-containers'; import { showSaveModal } from '@kbn/saved-objects-plugin/public'; -import { cloneDeep } from 'lodash'; -import React from 'react'; -import { batch } from 'react-redux'; -import { i18n } from '@kbn/i18n'; + import { DashboardContainerInput, DashboardPanelMap, @@ -29,8 +31,14 @@ import { DASHBOARD_CONTENT_ID, SAVED_OBJECT_POST_TIME } from '../../../dashboard import { SaveDashboardReturn, SavedDashboardInput, -} from '../../../services/dashboard_content_management/types'; -import { pluginServices } from '../../../services/plugin_services'; +} from '../../../services/dashboard_content_management_service/types'; +import { getDashboardContentManagementService } from '../../../services/dashboard_content_management_service'; +import { + coreServices, + dataService, + embeddableService, + savedObjectsTaggingService, +} from '../../../services/kibana_services'; import { DashboardSaveOptions, DashboardStateFromSaveModal } from '../../types'; import { DashboardContainer } from '../dashboard_container'; import { extractTitleAndCount } from './lib/extract_title_and_count'; @@ -39,9 +47,6 @@ import { DashboardSaveModal } from './overlays/save_modal'; const serializeAllPanelState = async ( dashboard: DashboardContainer ): Promise<{ panels: DashboardContainerInput['panels']; references: Reference[] }> => { - const { - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); const references: Reference[] = []; const panels = cloneDeep(dashboard.getInput().panels); @@ -49,7 +54,7 @@ const serializeAllPanelState = async ( Promise<{ uuid: string; serialized: SerializedPanelState }> > = []; for (const [uuid, panel] of Object.entries(panels)) { - if (!reactEmbeddableRegistryHasKey(panel.type)) continue; + if (!embeddableService.reactEmbeddableRegistryHasKey(panel.type)) continue; const api = dashboard.children$.value[uuid]; if (api && apiHasSerializableState(api)) { @@ -75,10 +80,6 @@ const serializeAllPanelState = async ( * Save the current state of this dashboard to a saved object without showing any save modal. */ export async function runQuickSave(this: DashboardContainer) { - const { - dashboardContentManagement: { saveDashboardState }, - } = pluginServices.getServices(); - const { explicitInput: currentState, componentState: { lastSavedId, managed }, @@ -98,7 +99,7 @@ export async function runQuickSave(this: DashboardContainer) { stateToSave = { ...stateToSave, controlGroupInput: controlGroupSerializedState }; } - const saveResult = await saveDashboardState({ + const saveResult = await getDashboardContentManagementService().saveDashboardState({ controlGroupReferences, panelReferences: references, currentState: stateToSave, @@ -118,20 +119,11 @@ export async function runQuickSave(this: DashboardContainer) { * accounts for scenarios of cloning elastic managed dashboard into user managed dashboards */ export async function runInteractiveSave(this: DashboardContainer, interactionMode: ViewMode) { - const { - data: { - query: { - timefilter: { timefilter }, - }, - }, - savedObjectsTagging: { hasApi: hasSavedObjectsTagging }, - dashboardContentManagement: { checkForDuplicateDashboardTitle, saveDashboardState }, - } = pluginServices.getServices(); - const { explicitInput: currentState, componentState: { lastSavedId, managed }, } = this.getState(); + const dashboardContentManagementService = getDashboardContentManagementService(); return new Promise((resolve, reject) => { if (interactionMode === ViewMode.EDIT && managed) { @@ -156,7 +148,7 @@ export async function runInteractiveSave(this: DashboardContainer, interactionMo try { if ( - !(await checkForDuplicateDashboardTitle({ + !(await dashboardContentManagementService.checkForDuplicateDashboardTitle({ title: newTitle, onTitleDuplicate, lastSavedTitle: currentState.title, @@ -172,11 +164,13 @@ export async function runInteractiveSave(this: DashboardContainer, interactionMo tags: [] as string[], description: newDescription, timeRestore: newTimeRestore, - timeRange: newTimeRestore ? timefilter.getTime() : undefined, - refreshInterval: newTimeRestore ? timefilter.getRefreshInterval() : undefined, + timeRange: newTimeRestore ? dataService.query.timefilter.timefilter.getTime() : undefined, + refreshInterval: newTimeRestore + ? dataService.query.timefilter.timefilter.getRefreshInterval() + : undefined, }; - if (hasSavedObjectsTagging && newTags) { + if (savedObjectsTaggingService && newTags) { // remove `hasSavedObjectsTagging` once the savedObjectsTagging service is optional stateFromSaveModal.tags = newTags; } @@ -226,7 +220,7 @@ export async function runInteractiveSave(this: DashboardContainer, interactionMo const beforeAddTime = window.performance.now(); - const saveResult = await saveDashboardState({ + const saveResult = await dashboardContentManagementService.saveDashboardState({ controlGroupReferences, panelReferences: references, saveOptions, @@ -240,7 +234,7 @@ export async function runInteractiveSave(this: DashboardContainer, interactionMo const addDuration = window.performance.now() - beforeAddTime; - reportPerformanceMetricEvent(pluginServices.getServices().analytics, { + reportPerformanceMetricEvent(coreServices.analytics, { eventName: SAVED_OBJECT_POST_TIME, duration: addDuration, meta: { @@ -279,7 +273,7 @@ export async function runInteractiveSave(this: DashboardContainer, interactionMo newTitle = `${baseTitle} (${baseCount + 1})`; - await checkForDuplicateDashboardTitle({ + await dashboardContentManagementService.checkForDuplicateDashboardTitle({ title: newTitle, lastSavedTitle: currentState.title, copyOnSave: true, diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.test.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.test.ts index 7a02b3479a2aa..1ecc06d90e84a 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.test.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.test.ts @@ -7,34 +7,34 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { EmbeddablePackageState, ViewMode } from '@kbn/embeddable-plugin/public'; import { + CONTACT_CARD_EMBEDDABLE, ContactCardEmbeddable, ContactCardEmbeddableFactory, ContactCardEmbeddableInput, ContactCardEmbeddableOutput, - CONTACT_CARD_EMBEDDABLE, } from '@kbn/embeddable-plugin/public/lib/test_samples'; import { Filter } from '@kbn/es-query'; -import { EmbeddablePackageState, ViewMode } from '@kbn/embeddable-plugin/public'; import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; -import { createDashboard } from './create_dashboard'; -import { getSampleDashboardPanel } from '../../../mocks'; -import { pluginServices } from '../../../services/plugin_services'; -import { DashboardCreationOptions } from '../dashboard_container_factory'; import { DEFAULT_DASHBOARD_INPUT } from '../../../dashboard_constants'; -import { mockControlGroupApi } from '../../../mocks'; +import { getSampleDashboardPanel, mockControlGroupApi } from '../../../mocks'; +import { dataService, embeddableService } from '../../../services/kibana_services'; +import { DashboardCreationOptions } from '../dashboard_container_factory'; +import { createDashboard } from './create_dashboard'; +import { getDashboardContentManagementService } from '../../../services/dashboard_content_management_service'; +import { getDashboardBackupService } from '../../../services/dashboard_backup_service'; + +const dashboardBackupService = getDashboardBackupService(); +const dashboardContentManagementService = getDashboardContentManagementService(); test("doesn't throw error when no data views are available", async () => { - pluginServices.getServices().data.dataViews.defaultDataViewExists = jest - .fn() - .mockReturnValue(false); + dataService.dataViews.defaultDataViewExists = jest.fn().mockReturnValue(false); expect(await createDashboard()).toBeDefined(); // reset get default data view - pluginServices.getServices().data.dataViews.defaultDataViewExists = jest - .fn() - .mockResolvedValue(true); + dataService.dataViews.defaultDataViewExists = jest.fn().mockResolvedValue(true); }); test('throws error when provided validation function returns invalid', async () => { @@ -73,79 +73,63 @@ test('does not get initial input when provided validation function returns redir }); test('pulls state from dashboard saved object when given a saved object id', async () => { - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - dashboardInput: { - ...DEFAULT_DASHBOARD_INPUT, - description: `wow would you look at that? Wow.`, - }, - }); + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + dashboardInput: { + ...DEFAULT_DASHBOARD_INPUT, + description: `wow would you look at that? Wow.`, + }, + }); const dashboard = await createDashboard({}, 0, 'wow-such-id'); - expect( - pluginServices.getServices().dashboardContentManagement.loadDashboardState - ).toHaveBeenCalledWith({ id: 'wow-such-id' }); + expect(dashboardContentManagementService.loadDashboardState).toHaveBeenCalledWith({ + id: 'wow-such-id', + }); expect(dashboard).toBeDefined(); expect(dashboard!.getState().explicitInput.description).toBe(`wow would you look at that? Wow.`); }); test('passes managed state from the saved object into the Dashboard component state', async () => { - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - dashboardInput: { - ...DEFAULT_DASHBOARD_INPUT, - description: 'wow this description is okay', - }, - managed: true, - }); + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + dashboardInput: { + ...DEFAULT_DASHBOARD_INPUT, + description: 'wow this description is okay', + }, + managed: true, + }); const dashboard = await createDashboard({}, 0, 'what-an-id'); expect(dashboard).toBeDefined(); expect(dashboard!.getState().componentState.managed).toBe(true); }); test('pulls view mode from dashboard backup', async () => { - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - dashboardInput: DEFAULT_DASHBOARD_INPUT, - }); - pluginServices.getServices().dashboardBackup.getViewMode = jest - .fn() - .mockReturnValue(ViewMode.EDIT); + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + dashboardInput: DEFAULT_DASHBOARD_INPUT, + }); + dashboardBackupService.getViewMode = jest.fn().mockReturnValue(ViewMode.EDIT); const dashboard = await createDashboard({ useSessionStorageIntegration: true }, 0, 'what-an-id'); expect(dashboard).toBeDefined(); expect(dashboard!.getState().explicitInput.viewMode).toBe(ViewMode.EDIT); }); test('new dashboards start in edit mode', async () => { - pluginServices.getServices().dashboardBackup.getViewMode = jest - .fn() - .mockReturnValue(ViewMode.VIEW); - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - newDashboardCreated: true, - dashboardInput: { - ...DEFAULT_DASHBOARD_INPUT, - description: 'wow this description is okay', - }, - }); + dashboardBackupService.getViewMode = jest.fn().mockReturnValue(ViewMode.VIEW); + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + newDashboardCreated: true, + dashboardInput: { + ...DEFAULT_DASHBOARD_INPUT, + description: 'wow this description is okay', + }, + }); const dashboard = await createDashboard({ useSessionStorageIntegration: true }, 0, 'wow-such-id'); expect(dashboard).toBeDefined(); expect(dashboard!.getState().explicitInput.viewMode).toBe(ViewMode.EDIT); }); test('managed dashboards start in view mode', async () => { - pluginServices.getServices().dashboardBackup.getViewMode = jest - .fn() - .mockReturnValue(ViewMode.EDIT); - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - dashboardInput: DEFAULT_DASHBOARD_INPUT, - managed: true, - }); + dashboardBackupService.getViewMode = jest.fn().mockReturnValue(ViewMode.EDIT); + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + dashboardInput: DEFAULT_DASHBOARD_INPUT, + managed: true, + }); const dashboard = await createDashboard({}, 0, 'what-an-id'); expect(dashboard).toBeDefined(); expect(dashboard!.getState().componentState.managed).toBe(true); @@ -153,15 +137,13 @@ test('managed dashboards start in view mode', async () => { }); test('pulls state from backup which overrides state from saved object', async () => { - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - dashboardInput: { - ...DEFAULT_DASHBOARD_INPUT, - description: 'wow this description is okay', - }, - }); - pluginServices.getServices().dashboardBackup.getState = jest + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + dashboardInput: { + ...DEFAULT_DASHBOARD_INPUT, + description: 'wow this description is okay', + }, + }); + dashboardBackupService.getState = jest .fn() .mockReturnValue({ dashboardState: { description: 'wow this description marginally better' } }); const dashboard = await createDashboard({ useSessionStorageIntegration: true }, 0, 'wow-such-id'); @@ -172,15 +154,13 @@ test('pulls state from backup which overrides state from saved object', async () }); test('pulls state from override input which overrides all other state sources', async () => { - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - dashboardInput: { - ...DEFAULT_DASHBOARD_INPUT, - description: 'wow this description is okay', - }, - }); - pluginServices.getServices().dashboardBackup.getState = jest + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + dashboardInput: { + ...DEFAULT_DASHBOARD_INPUT, + description: 'wow this description is okay', + }, + }); + dashboardBackupService.getState = jest .fn() .mockReturnValue({ description: 'wow this description marginally better' }); const dashboard = await createDashboard( @@ -198,35 +178,33 @@ test('pulls state from override input which overrides all other state sources', }); test('pulls panels from override input', async () => { - pluginServices.getServices().embeddable.reactEmbeddableRegistryHasKey = jest + embeddableService.reactEmbeddableRegistryHasKey = jest .fn() .mockImplementation((type: string) => type === 'reactEmbeddable'); - pluginServices.getServices().dashboardContentManagement.loadDashboardState = jest - .fn() - .mockResolvedValue({ - dashboardInput: { - ...DEFAULT_DASHBOARD_INPUT, - panels: { - ...DEFAULT_DASHBOARD_INPUT.panels, - someLegacyPanel: { - type: 'legacy', - gridData: { x: 0, y: 0, w: 0, h: 0, i: 'someLegacyPanel' }, - explicitInput: { - id: 'someLegacyPanel', - title: 'stateFromSavedObject', - }, + dashboardContentManagementService.loadDashboardState = jest.fn().mockResolvedValue({ + dashboardInput: { + ...DEFAULT_DASHBOARD_INPUT, + panels: { + ...DEFAULT_DASHBOARD_INPUT.panels, + someLegacyPanel: { + type: 'legacy', + gridData: { x: 0, y: 0, w: 0, h: 0, i: 'someLegacyPanel' }, + explicitInput: { + id: 'someLegacyPanel', + title: 'stateFromSavedObject', }, - someReactEmbeddablePanel: { - type: 'reactEmbeddable', - gridData: { x: 0, y: 0, w: 0, h: 0, i: 'someReactEmbeddablePanel' }, - explicitInput: { - id: 'someReactEmbeddablePanel', - title: 'stateFromSavedObject', - }, + }, + someReactEmbeddablePanel: { + type: 'reactEmbeddable', + gridData: { x: 0, y: 0, w: 0, h: 0, i: 'someReactEmbeddablePanel' }, + explicitInput: { + id: 'someReactEmbeddablePanel', + title: 'stateFromSavedObject', }, }, }, - }); + }, + }); const dashboard = await createDashboard( { useSessionStorageIntegration: true, @@ -286,10 +264,8 @@ test('applies filters and query from state to query service', async () => { }, getInitialInput: () => ({ filters, query }), }); - expect(pluginServices.getServices().data.query.queryString.setQuery).toHaveBeenCalledWith(query); - expect(pluginServices.getServices().data.query.filterManager.setAppFilters).toHaveBeenCalledWith( - filters - ); + expect(dataService.query.queryString.setQuery).toHaveBeenCalledWith(query); + expect(dataService.query.filterManager.setAppFilters).toHaveBeenCalledWith(filters); }); test('applies time range and refresh interval from initial input to query service if time restore is on', async () => { @@ -302,20 +278,16 @@ test('applies time range and refresh interval from initial input to query servic }, getInitialInput: () => ({ timeRange, refreshInterval, timeRestore: true }), }); - expect( - pluginServices.getServices().data.query.timefilter.timefilter.setTime - ).toHaveBeenCalledWith(timeRange); - expect( - pluginServices.getServices().data.query.timefilter.timefilter.setRefreshInterval - ).toHaveBeenCalledWith(refreshInterval); + expect(dataService.query.timefilter.timefilter.setTime).toHaveBeenCalledWith(timeRange); + expect(dataService.query.timefilter.timefilter.setRefreshInterval).toHaveBeenCalledWith( + refreshInterval + ); }); test('applies time range from query service to initial input if time restore is on but there is an explicit time range in the URL', async () => { const urlTimeRange = { from: new Date().toISOString(), to: new Date().toISOString() }; const savedTimeRange = { from: 'now - 7 days', to: 'now' }; - pluginServices.getServices().data.query.timefilter.timefilter.getTime = jest - .fn() - .mockReturnValue(urlTimeRange); + dataService.query.timefilter.timefilter.getTime = jest.fn().mockReturnValue(urlTimeRange); const kbnUrlStateStorage = createKbnUrlStateStorage(); kbnUrlStateStorage.get = jest.fn().mockReturnValue({ time: urlTimeRange }); @@ -335,9 +307,7 @@ test('applies time range from query service to initial input if time restore is test('applies time range from query service to initial input if time restore is off', async () => { const timeRange = { from: new Date().toISOString(), to: new Date().toISOString() }; - pluginServices.getServices().data.query.timefilter.timefilter.getTime = jest - .fn() - .mockReturnValue(timeRange); + dataService.query.timefilter.timefilter.getTime = jest.fn().mockReturnValue(timeRange); const dashboard = await createDashboard({ useUnifiedSearchIntegration: true, unifiedSearchSettings: { @@ -393,9 +363,7 @@ test('creates new embeddable with incoming embeddable if id does not match exist create: jest.fn().mockReturnValue({ destroy: jest.fn() }), getDefaultInput: jest.fn().mockResolvedValue({}), }; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockContactCardFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockContactCardFactory); const dashboard = await createDashboard({ getIncomingEmbeddable: () => incomingEmbeddable, @@ -454,9 +422,7 @@ test('creates new embeddable with specified size if size is provided', async () create: jest.fn().mockReturnValue({ destroy: jest.fn() }), getDefaultInput: jest.fn().mockResolvedValue({}), }; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockContactCardFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockContactCardFactory); const dashboard = await createDashboard({ getIncomingEmbeddable: () => incomingEmbeddable, @@ -513,11 +479,9 @@ test('searchSessionId is updated prior to child embeddable parent subscription e }, }), }; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(embeddableFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(embeddableFactory); let sessionCount = 0; - pluginServices.getServices().data.search.session.start = () => { + dataService.search.session.start = () => { sessionCount++; return `searchSessionId${sessionCount}`; }; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts index 48a9a82838462..2b44abe481b7d 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts @@ -7,13 +7,16 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { cloneDeep, omit } from 'lodash'; +import { Subject } from 'rxjs'; +import { v4 } from 'uuid'; + +import { ContentInsightsClient } from '@kbn/content-management-content-insights-public'; import { GlobalQueryStateFromUrl, syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { TimeRange } from '@kbn/es-query'; import { lazyLoadReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; -import { cloneDeep, omit } from 'lodash'; -import { Subject } from 'rxjs'; -import { v4 } from 'uuid'; + import { DashboardContainerInput, DashboardPanelMap, @@ -26,11 +29,17 @@ import { GLOBAL_STATE_STORAGE_KEY, PanelPlacementStrategy, } from '../../../dashboard_constants'; +import { + PANELS_CONTROL_GROUP_KEY, + getDashboardBackupService, +} from '../../../services/dashboard_backup_service'; +import { getDashboardContentManagementService } from '../../../services/dashboard_content_management_service'; import { LoadDashboardReturn, SavedDashboardInput, -} from '../../../services/dashboard_content_management/types'; -import { pluginServices } from '../../../services/plugin_services'; +} from '../../../services/dashboard_content_management_service/types'; +import { coreServices, dataService, embeddableService } from '../../../services/kibana_services'; +import { getDashboardCapabilities } from '../../../utils/get_dashboard_capabilities'; import { runPanelPlacementStrategy } from '../../panel_placement/place_new_panel_strategies'; import { startDiffingDashboardState } from '../../state/diffing/dashboard_diffing_integration'; import { DashboardPublicState, UnsavedPanelState } from '../../types'; @@ -40,7 +49,6 @@ import { startSyncingDashboardDataViews } from './data_views/sync_dashboard_data import { startQueryPerformanceTracking } from './performance/query_performance_tracking'; import { startDashboardSearchSessionIntegration } from './search_sessions/start_dashboard_search_session_integration'; import { syncUnifiedSearchState } from './unified_search/sync_dashboard_unified_search_state'; -import { PANELS_CONTROL_GROUP_KEY } from '../../../services/dashboard_backup/dashboard_backup_service'; /** * Builds a new Dashboard from scratch. @@ -50,11 +58,6 @@ export const createDashboard = async ( dashboardCreationStartTime?: number, savedObjectId?: string ): Promise => { - const { - data: { dataViews }, - dashboardContentManagement: { loadDashboardState }, - } = pluginServices.getServices(); - // -------------------------------------------------------------------------------------- // Create method which allows work to be done on the dashboard container when it's ready. // -------------------------------------------------------------------------------------- @@ -71,8 +74,11 @@ export const createDashboard = async ( // Lazy load required systems and Dashboard saved object. // -------------------------------------------------------------------------------------- const reduxEmbeddablePackagePromise = lazyLoadReduxToolsPackage(); - const defaultDataViewExistsPromise = dataViews.defaultDataViewExists(); - const dashboardSavedObjectPromise = loadDashboardState({ id: savedObjectId }); + const defaultDataViewExistsPromise = dataService.dataViews.defaultDataViewExists(); + const dashboardContentManagementService = getDashboardContentManagementService(); + const dashboardSavedObjectPromise = dashboardContentManagementService.loadDashboardState({ + id: savedObjectId, + }); const [reduxEmbeddablePackage, savedObjectResult] = await Promise.all([ reduxEmbeddablePackagePromise, @@ -140,21 +146,12 @@ export const initializeDashboard = async ({ untilDashboardReady: () => Promise; creationOptions?: DashboardCreationOptions; }) => { - const { - dashboardBackup, - dashboardCapabilities: { showWriteControls }, - embeddable: { reactEmbeddableRegistryHasKey }, - data: { - query: queryService, - search: { session }, - }, - dashboardContentInsights, - } = pluginServices.getServices(); const { queryString, filterManager, timefilter: { timefilter: timefilterService }, - } = queryService; + } = dataService.query; + const dashboardBackupService = getDashboardBackupService(); const { getInitialInput, @@ -179,7 +176,7 @@ export const initializeDashboard = async ({ // -------------------------------------------------------------------------------------- // Combine input from saved object, and session storage // -------------------------------------------------------------------------------------- - const dashboardBackupState = dashboardBackup.getState(loadDashboardReturn.dashboardId); + const dashboardBackupState = dashboardBackupService.getState(loadDashboardReturn.dashboardId); const runtimePanelsToRestore: UnsavedPanelState = useSessionStorageIntegration ? dashboardBackupState?.panels ?? {} : {}; @@ -189,15 +186,16 @@ export const initializeDashboard = async ({ return dashboardBackupState?.dashboardState; })(); const initialViewMode = (() => { - if (loadDashboardReturn.managed || !showWriteControls) return ViewMode.VIEW; + if (loadDashboardReturn.managed || !getDashboardCapabilities().showWriteControls) + return ViewMode.VIEW; if ( loadDashboardReturn.newDashboardCreated || - dashboardBackup.dashboardHasUnsavedEdits(loadDashboardReturn.dashboardId) + dashboardBackupService.dashboardHasUnsavedEdits(loadDashboardReturn.dashboardId) ) { return ViewMode.EDIT; } - return dashboardBackup.getViewMode(); + return dashboardBackupService.getViewMode(); })(); const combinedSessionInput: DashboardContainerInput = { @@ -218,7 +216,7 @@ export const initializeDashboard = async ({ const overridePanels: DashboardPanelMap = {}; for (const panel of Object.values(overrideInput?.panels)) { - if (reactEmbeddableRegistryHasKey(panel.type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(panel.type)) { overridePanels[panel.explicitInput.id] = { ...panel, @@ -263,7 +261,7 @@ export const initializeDashboard = async ({ // Back up any view mode passed in explicitly. if (overrideInput?.viewMode) { - dashboardBackup.storeViewMode(overrideInput?.viewMode); + dashboardBackupService.storeViewMode(overrideInput?.viewMode); } initialDashboardInput.executionContext = { @@ -319,7 +317,7 @@ export const initializeDashboard = async ({ // start syncing global query state with the URL. const { stop: stopSyncingQueryServiceStateWithUrl } = syncGlobalQueryStateWithUrl( - queryService, + dataService.query, kbnUrlStateStorage ); @@ -363,7 +361,7 @@ export const initializeDashboard = async ({ // maintain hide panel titles setting. hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles, }; - if (reactEmbeddableRegistryHasKey(incomingEmbeddable.type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(incomingEmbeddable.type)) { panelToUpdate.explicitInput = { id: panelToUpdate.explicitInput.id }; runtimePanelsToRestore[incomingEmbeddable.embeddableId] = nextRuntimeState; } else { @@ -399,7 +397,7 @@ export const initializeDashboard = async ({ } ); const newPanelState: DashboardPanelState = (() => { - if (reactEmbeddableRegistryHasKey(incomingEmbeddable.type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(incomingEmbeddable.type)) { runtimePanelsToRestore[embeddableId] = incomingEmbeddable.input; return { explicitInput: { id: embeddableId }, @@ -487,16 +485,18 @@ export const initializeDashboard = async ({ // if this incoming embeddable has a session, continue it. if (incomingEmbeddable?.searchSessionId) { - session.continue(incomingEmbeddable.searchSessionId); + dataService.search.session.continue(incomingEmbeddable.searchSessionId); } if (sessionIdToRestore) { - session.restore(sessionIdToRestore); + dataService.search.session.restore(sessionIdToRestore); } - const existingSession = session.getSessionId(); + const existingSession = dataService.search.session.getSessionId(); initialSearchSessionId = sessionIdToRestore ?? - (existingSession && incomingEmbeddable ? existingSession : session.start()); + (existingSession && incomingEmbeddable + ? existingSession + : dataService.search.session.start()); untilDashboardReady().then(async (container) => { await container.untilContainerInitialized(); @@ -511,7 +511,11 @@ export const initializeDashboard = async ({ // We don't count views when a user is editing a dashboard and is returning from an editor after saving // however, there is an edge case that we now count a new view when a user is editing a dashboard and is returning from an editor by canceling // TODO: this should be revisited by making embeddable transfer support canceling logic https://github.com/elastic/kibana/issues/190485 - dashboardContentInsights.trackDashboardView(loadDashboardReturn.dashboardId); + const contentInsightsClient = new ContentInsightsClient( + { http: coreServices.http }, + { domainId: 'dashboard' } + ); + contentInsightsClient.track(loadDashboardReturn.dashboardId, 'viewed'); } return { input: initialDashboardInput, searchSessionId: initialSearchSessionId }; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/data_views/sync_dashboard_data_views.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/data_views/sync_dashboard_data_views.ts index 8954155d80939..3060987e296c6 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/data_views/sync_dashboard_data_views.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/data_views/sync_dashboard_data_views.ts @@ -7,19 +7,17 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { uniqBy } from 'lodash'; +import { combineLatest, Observable, of, switchMap } from 'rxjs'; + import { DataView } from '@kbn/data-views-plugin/common'; import { combineCompatibleChildrenApis } from '@kbn/presentation-containers'; import { apiPublishesDataViews, PublishesDataViews } from '@kbn/presentation-publishing'; -import { uniqBy } from 'lodash'; -import { combineLatest, Observable, of, switchMap } from 'rxjs'; -import { pluginServices } from '../../../../services/plugin_services'; + +import { dataService } from '../../../../services/kibana_services'; import { DashboardContainer } from '../../dashboard_container'; export function startSyncingDashboardDataViews(this: DashboardContainer) { - const { - data: { dataViews }, - } = pluginServices.getServices(); - const controlGroupDataViewsPipe: Observable = this.controlGroupApi$.pipe( switchMap((controlGroupApi) => { return controlGroupApi ? controlGroupApi.dataViews : of([]); @@ -42,8 +40,8 @@ export function startSyncingDashboardDataViews(this: DashboardContainer) { ]; if (allDataViews.length === 0) { return (async () => { - const defaultDataViewId = await dataViews.getDefaultId(); - return [await dataViews.get(defaultDataViewId!)]; + const defaultDataViewId = await dataService.dataViews.getDefaultId(); + return [await dataService.dataViews.get(defaultDataViewId!)]; })(); } return of(uniqBy(allDataViews, 'id')); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.test.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.test.ts index 2caf5af31164c..963914bea1c33 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.test.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.test.ts @@ -7,18 +7,18 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { CoreStart } from '@kbn/core/public'; import { PerformanceMetricEvent } from '@kbn/ebt-tools'; import { PresentationContainer, TracksQueryPerformance } from '@kbn/presentation-containers'; import { getMockPresentationContainer } from '@kbn/presentation-containers/mocks'; -import { apiPublishesPhaseEvents, PhaseEvent, PhaseEventType } from '@kbn/presentation-publishing'; +import { PhaseEvent, PhaseEventType, apiPublishesPhaseEvents } from '@kbn/presentation-publishing'; import { waitFor } from '@testing-library/react'; import { BehaviorSubject } from 'rxjs'; -import { DashboardAnalyticsService } from '../../../../services/analytics/types'; import { startQueryPerformanceTracking } from './query_performance_tracking'; const mockMetricEvent = jest.fn(); jest.mock('@kbn/ebt-tools', () => ({ - reportPerformanceMetricEvent: (_: DashboardAnalyticsService, args: PerformanceMetricEvent) => { + reportPerformanceMetricEvent: (_: CoreStart['analytics'], args: PerformanceMetricEvent) => { mockMetricEvent(args); }, })); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.ts index a8ea06d25effa..ecf990675483a 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/performance/query_performance_tracking.ts @@ -7,12 +7,14 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { combineLatest, map, of, pairwise, startWith, switchMap } from 'rxjs'; + import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { PresentationContainer, TracksQueryPerformance } from '@kbn/presentation-containers'; -import { apiPublishesPhaseEvents, PublishesPhaseEvents } from '@kbn/presentation-publishing'; -import { combineLatest, map, of, pairwise, startWith, switchMap } from 'rxjs'; +import { PublishesPhaseEvents, apiPublishesPhaseEvents } from '@kbn/presentation-publishing'; + import { DASHBOARD_LOADED_EVENT } from '../../../../dashboard_constants'; -import { pluginServices } from '../../../../services/plugin_services'; +import { coreServices } from '../../../../services/kibana_services'; import { DashboardLoadType } from '../../../types'; let isFirstDashboardLoadOfSession = true; @@ -26,7 +28,6 @@ const loadTypesMapping: { [key in DashboardLoadType]: number } = { export const startQueryPerformanceTracking = ( dashboard: PresentationContainer & TracksQueryPerformance ) => { - const { analytics } = pluginServices.getServices(); const reportPerformanceMetrics = ({ timeToData, panelCount, @@ -41,7 +42,7 @@ export const startQueryPerformanceTracking = ( const duration = loadType === 'dashboardSubsequentLoad' ? timeToData : Math.max(timeToData, totalLoadTime); - reportPerformanceMetricEvent(analytics, { + reportPerformanceMetricEvent(coreServices.analytics, { eventName: DASHBOARD_LOADED_EVENT, duration, key1: 'time_to_data', diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/search_sessions/start_dashboard_search_session_integration.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/search_sessions/start_dashboard_search_session_integration.ts index ee74efbb75d56..8150ee7a72381 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/search_sessions/start_dashboard_search_session_integration.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/search_sessions/start_dashboard_search_session_integration.ts @@ -11,10 +11,11 @@ import { skip } from 'rxjs'; import { noSearchSessionStorageCapabilityMessage } from '@kbn/data-plugin/public'; +import { dataService } from '../../../../services/kibana_services'; import { DashboardContainer } from '../../dashboard_container'; -import { pluginServices } from '../../../../services/plugin_services'; import { DashboardCreationOptions } from '../../dashboard_container_factory'; import { newSession$ } from './new_session'; +import { getDashboardCapabilities } from '../../../../utils/get_dashboard_capabilities'; /** * Enables dashboard search sessions. @@ -25,13 +26,6 @@ export function startDashboardSearchSessionIntegration( ) { if (!searchSessionSettings) return; - const { - data: { - search: { session }, - }, - dashboardCapabilities: { storeSearchSession: canStoreSearchSession }, - } = pluginServices.getServices(); - const { sessionIdUrlChangeObservable, getSearchSessionIdFromURL, @@ -39,9 +33,9 @@ export function startDashboardSearchSessionIntegration( createSessionRestorationDataProvider, } = searchSessionSettings; - session.enableStorage(createSessionRestorationDataProvider(this), { + dataService.search.session.enableStorage(createSessionRestorationDataProvider(this), { isDisabled: () => - canStoreSearchSession + getDashboardCapabilities().storeSearchSession ? { disabled: false } : { disabled: true, @@ -60,15 +54,18 @@ export function startDashboardSearchSessionIntegration( const updatedSearchSessionId: string | undefined = (() => { let searchSessionIdFromURL = getSearchSessionIdFromURL(); if (searchSessionIdFromURL) { - if (session.isRestore() && session.isCurrentSession(searchSessionIdFromURL)) { + if ( + dataService.search.session.isRestore() && + dataService.search.session.isCurrentSession(searchSessionIdFromURL) + ) { // we had previously been in a restored session but have now changed state so remove the session id from the URL. removeSessionIdFromUrl(); searchSessionIdFromURL = undefined; } else { - session.restore(searchSessionIdFromURL); + dataService.search.session.restore(searchSessionIdFromURL); } } - return searchSessionIdFromURL ?? session.start(); + return searchSessionIdFromURL ?? dataService.search.session.start(); })(); if (updatedSearchSessionId && updatedSearchSessionId !== currentSearchSessionId) { diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/unified_search/sync_dashboard_unified_search_state.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/unified_search/sync_dashboard_unified_search_state.ts index 184b97c893a2c..160af8a005b1a 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/create/unified_search/sync_dashboard_unified_search_state.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/unified_search/sync_dashboard_unified_search_state.ts @@ -21,9 +21,9 @@ import { } from '@kbn/data-plugin/public'; import { DashboardContainer } from '../../dashboard_container'; -import { pluginServices } from '../../../../services/plugin_services'; import { GLOBAL_STATE_STORAGE_KEY } from '../../../../dashboard_constants'; import { areTimesEqual } from '../../../state/diffing/dashboard_diffing_utils'; +import { dataService } from '../../../../services/kibana_services'; /** * Sets up syncing and subscriptions between the filter state from the Data plugin @@ -33,11 +33,7 @@ export function syncUnifiedSearchState( this: DashboardContainer, kbnUrlStateStorage: IKbnUrlStateStorage ) { - const { - data: { query: queryService, search }, - } = pluginServices.getServices(); - const { queryString, timefilter } = queryService; - const { timefilter: timefilterService } = timefilter; + const timefilterService = dataService.query.timefilter.timefilter; // get Observable for when the dashboard's saved filters or query change. const OnFiltersChange$ = new Subject<{ filters: Filter[]; query: Query }>(); @@ -47,7 +43,7 @@ export function syncUnifiedSearchState( } = this.getState(); OnFiltersChange$.next({ filters: filters ?? [], - query: query ?? queryString.getDefaultQuery(), + query: query ?? dataService.query.queryString.getDefaultQuery(), }); }); @@ -56,12 +52,12 @@ export function syncUnifiedSearchState( explicitInput: { filters, query }, } = this.getState(); const intermediateFilterState: { filters: Filter[]; query: Query } = { - query: query ?? queryString.getDefaultQuery(), + query: query ?? dataService.query.queryString.getDefaultQuery(), filters: filters ?? [], }; const stopSyncingAppFilters = connectToQueryState( - queryService, + dataService.query, { get: () => intermediateFilterState, set: ({ filters: newFilters, query: newQuery }) => { @@ -144,7 +140,7 @@ export function syncUnifiedSearchState( }), switchMap((done) => // best way on a dashboard to estimate that panels are updated is to rely on search session service state - waitUntilNextSessionCompletes$(search.session).pipe(finalize(done)) + waitUntilNextSessionCompletes$(dataService.search.session).pipe(finalize(done)) ) ) .subscribe(); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx index eb798049ec48a..83efeab804ba5 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx @@ -9,11 +9,11 @@ import { isErrorEmbeddable, ViewMode } from '@kbn/embeddable-plugin/public'; import { + CONTACT_CARD_EMBEDDABLE, ContactCardEmbeddable, ContactCardEmbeddableFactory, ContactCardEmbeddableInput, ContactCardEmbeddableOutput, - CONTACT_CARD_EMBEDDABLE, EMPTY_EMBEDDABLE, } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import type { TimeRange } from '@kbn/es-query'; @@ -25,13 +25,11 @@ import { getSampleDashboardPanel, mockControlGroupApi, } from '../../mocks'; -import { pluginServices } from '../../services/plugin_services'; +import { embeddableService } from '../../services/kibana_services'; import { DashboardContainer } from './dashboard_container'; const embeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); -pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(embeddableFactory); +embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(embeddableFactory); test('DashboardContainer initializes embeddables', (done) => { const container = buildMockDashboard({ diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index 43f733895f1f5..161b2f93f12a2 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -7,59 +7,69 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import deepEqual from 'fast-deep-equal'; +import { omit } from 'lodash'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { batch } from 'react-redux'; +import { + BehaviorSubject, + Subject, + Subscription, + distinctUntilChanged, + first, + map, + skipWhile, + switchMap, +} from 'rxjs'; +import { v4 } from 'uuid'; + import { METRIC_TYPE } from '@kbn/analytics'; import type { Reference } from '@kbn/content-management-utils'; -import type { I18nStart, KibanaExecutionContext, OverlayRef } from '@kbn/core/public'; -import { - type PublishingSubject, - apiPublishesPanelTitle, - apiPublishesUnsavedChanges, - getPanelTitle, - PublishesViewMode, - PublishesDataLoading, - apiPublishesDataLoading, -} from '@kbn/presentation-publishing'; +import { ControlGroupApi, ControlGroupSerializedState } from '@kbn/controls-plugin/public'; +import type { KibanaExecutionContext, OverlayRef } from '@kbn/core/public'; import { RefreshInterval } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { Container, DefaultEmbeddableApi, EmbeddableFactoryNotFoundError, - embeddableInputToSubject, - isExplicitInputWithAttributes, PanelNotFoundError, ViewMode, + embeddableInputToSubject, + isExplicitInputWithAttributes, type EmbeddableFactory, type EmbeddableInput, type EmbeddableOutput, type IEmbeddable, } from '@kbn/embeddable-plugin/public'; import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { HasRuntimeChildState, HasSaveNotification, HasSerializedChildState, + PanelPackage, TrackContentfulRender, TracksQueryPerformance, combineCompatibleChildrenApis, } from '@kbn/presentation-containers'; -import { PanelPackage } from '@kbn/presentation-containers'; +import { PublishesSettings } from '@kbn/presentation-containers/interfaces/publishes_settings'; +import { apiHasSerializableState } from '@kbn/presentation-containers/interfaces/serialized_state'; +import { + PublishesDataLoading, + PublishesViewMode, + apiPublishesDataLoading, + apiPublishesPanelTitle, + apiPublishesUnsavedChanges, + getPanelTitle, + type PublishingSubject, +} from '@kbn/presentation-publishing'; import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { LocatorPublic } from '@kbn/share-plugin/common'; import { ExitFullScreenButtonKibanaProvider } from '@kbn/shared-ux-button-exit-full-screen'; -import deepEqual from 'fast-deep-equal'; -import { omit } from 'lodash'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import { batch } from 'react-redux'; -import { BehaviorSubject, Subject, Subscription, first, skipWhile, switchMap } from 'rxjs'; -import { distinctUntilChanged, map } from 'rxjs'; -import { v4 } from 'uuid'; -import { PublishesSettings } from '@kbn/presentation-containers/interfaces/publishes_settings'; -import { apiHasSerializableState } from '@kbn/presentation-containers/interfaces/serialized_state'; -import { ControlGroupApi, ControlGroupSerializedState } from '@kbn/controls-plugin/public'; -import { DashboardLocatorParams, DASHBOARD_CONTAINER_TYPE, DashboardApi } from '../..'; + +import { DASHBOARD_CONTAINER_TYPE, DashboardApi, DashboardLocatorParams } from '../..'; import { DashboardAttributes, DashboardContainerInput, @@ -70,6 +80,8 @@ import { getReferencesForControls, getReferencesForPanelId, } from '../../../common/dashboard_container/persistable_state/dashboard_container_references'; +import { DashboardContext } from '../../dashboard_api/use_dashboard_api'; +import { getPanelAddedSuccessString } from '../../dashboard_app/_dashboard_app_strings'; import { DASHBOARD_APP_ID, DASHBOARD_UI_METRIC_ID, @@ -77,13 +89,19 @@ import { DEFAULT_PANEL_WIDTH, PanelPlacementStrategy, } from '../../dashboard_constants'; -import { DashboardAnalyticsService } from '../../services/analytics/types'; -import { DashboardCapabilitiesService } from '../../services/dashboard_capabilities/types'; -import { pluginServices } from '../../services/plugin_services'; -import { placePanel } from '../panel_placement'; -import { runPanelPlacementStrategy } from '../panel_placement/place_new_panel_strategies'; +import { PANELS_CONTROL_GROUP_KEY } from '../../services/dashboard_backup_service'; +import { getDashboardContentManagementService } from '../../services/dashboard_content_management_service'; +import { + coreServices, + dataService, + embeddableService, + usageCollectionService, +} from '../../services/kibana_services'; +import { getDashboardCapabilities } from '../../utils/get_dashboard_capabilities'; import { DashboardViewport } from '../component/viewport/dashboard_viewport'; +import { placePanel } from '../panel_placement'; import { getDashboardPanelPlacementSetting } from '../panel_placement/panel_placement_registry'; +import { runPanelPlacementStrategy } from '../panel_placement/place_new_panel_strategies'; import { dashboardContainerReducers } from '../state/dashboard_container_reducers'; import { getDiffingMiddleware } from '../state/diffing/dashboard_diffing_integration'; import { @@ -92,7 +110,7 @@ import { DashboardStateFromSettingsFlyout, UnsavedPanelState, } from '../types'; -import { addFromLibrary, addOrUpdateEmbeddable, runQuickSave, runInteractiveSave } from './api'; +import { addFromLibrary, addOrUpdateEmbeddable, runInteractiveSave, runQuickSave } from './api'; import { duplicateDashboardPanel } from './api/duplicate_dashboard_panel'; import { combineDashboardFiltersWithControlGroupFilters, @@ -104,9 +122,6 @@ import { dashboardTypeDisplayLowercase, dashboardTypeDisplayName, } from './dashboard_container_factory'; -import { getPanelAddedSuccessString } from '../../dashboard_app/_dashboard_app_strings'; -import { PANELS_CONTROL_GROUP_KEY } from '../../services/dashboard_backup/dashboard_backup_service'; -import { DashboardContext } from '../../dashboard_api/use_dashboard_api'; export interface InheritedChildInput { filters: Filter[]; @@ -186,16 +201,11 @@ export class DashboardContainer // Services that are used in the Dashboard container code private creationOptions?: DashboardCreationOptions; - private analyticsService: DashboardAnalyticsService; - private showWriteControls: DashboardCapabilitiesService['showWriteControls']; - private i18n: I18nStart; - private theme; - private chrome; - private customBranding; + private showWriteControls: boolean; public trackContentfulRender() { - if (!this.hadContentfulRender && this.analyticsService) { - this.analyticsService.reportEvent('dashboard_loaded_with_data', {}); + if (!this.hadContentfulRender) { + coreServices.analytics.reportEvent('dashboard_loaded_with_data', {}); } this.hadContentfulRender = true; } @@ -238,37 +248,26 @@ export class DashboardContainer }); } - const { - usageCollection, - embeddable: { getEmbeddableFactory }, - } = pluginServices.getServices(); - super( { ...initialInput, }, { embeddableLoaded: {} }, - getEmbeddableFactory, + embeddableService.getEmbeddableFactory, parent, { untilContainerInitialized } ); + ({ showWriteControls: this.showWriteControls } = getDashboardCapabilities()); + this.controlGroupApi$ = controlGroupApi$; this.untilContainerInitialized = untilContainerInitialized; - this.trackPanelAddMetric = usageCollection.reportUiCounter?.bind( - usageCollection, + this.trackPanelAddMetric = usageCollectionService?.reportUiCounter.bind( + usageCollectionService, DASHBOARD_UI_METRIC_ID ); - ({ - analytics: this.analyticsService, - settings: { theme: this.theme, i18n: this.i18n }, - chrome: this.chrome, - customBranding: this.customBranding, - dashboardCapabilities: { showWriteControls: this.showWriteControls }, - } = pluginServices.getServices()); - this.creationOptions = creationOptions; this.searchSessionId = initialSessionId; this.searchSessionId$.next(initialSessionId); @@ -475,12 +474,12 @@ export class DashboardContainer ReactDOM.render( @@ -610,14 +609,9 @@ export class DashboardContainer panelPackage: PanelPackage, displaySuccessMessage?: boolean ) { - const { - notifications: { toasts }, - embeddable: { getEmbeddableFactory, reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); - const onSuccess = (id?: string, title?: string) => { if (!displaySuccessMessage) return; - toasts.addSuccess({ + coreServices.notifications.toasts.addSuccess({ title: getPanelAddedSuccessString(title), 'data-test-subj': 'addEmbeddableToDashboardSuccess', }); @@ -628,10 +622,10 @@ export class DashboardContainer if (this.trackPanelAddMetric) { this.trackPanelAddMetric(METRIC_TYPE.CLICK, panelPackage.panelType); } - if (reactEmbeddableRegistryHasKey(panelPackage.panelType)) { + if (embeddableService.reactEmbeddableRegistryHasKey(panelPackage.panelType)) { const newId = v4(); - const getCustomPlacementSettingFunc = await getDashboardPanelPlacementSetting( + const getCustomPlacementSettingFunc = getDashboardPanelPlacementSetting( panelPackage.panelType ); @@ -671,7 +665,7 @@ export class DashboardContainer return await this.untilReactEmbeddableLoaded(newId); } - const embeddableFactory = getEmbeddableFactory(panelPackage.panelType); + const embeddableFactory = embeddableService.getEmbeddableFactory(panelPackage.panelType); if (!embeddableFactory) { throw new EmbeddableFactoryNotFoundError(panelPackage.panelType); } @@ -709,11 +703,8 @@ export class DashboardContainer } public getDashboardPanelFromId = async (panelId: string) => { - const { - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); const panel = this.getInput().panels[panelId]; - if (reactEmbeddableRegistryHasKey(panel.type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(panel.type)) { const child = this.children$.value[panelId]; if (!child) throw new PanelNotFoundError(); const serialized = apiHasSerializableState(child) @@ -769,13 +760,7 @@ export class DashboardContainer // if we are using the unified search integration, we need to force reset the time picker. if (this.creationOptions?.useUnifiedSearchIntegration && lastSavedTimeRestore) { - const { - data: { - query: { - timefilter: { timefilter: timeFilterService }, - }, - }, - } = pluginServices.getServices(); + const timeFilterService = dataService.query.timefilter.timefilter; if (timeRange) timeFilterService.setTime(timeRange); if (refreshInterval) timeFilterService.setRefreshInterval(refreshInterval); } @@ -790,13 +775,12 @@ export class DashboardContainer this.integrationSubscriptions = new Subscription(); this.stopSyncingWithUnifiedSearch?.(); - const { - dashboardContentManagement: { loadDashboardState }, - } = pluginServices.getServices(); if (newCreationOptions) { this.creationOptions = { ...this.creationOptions, ...newCreationOptions }; } - const loadDashboardReturn = await loadDashboardState({ id: newSavedObjectId }); + const loadDashboardReturn = await getDashboardContentManagementService().loadDashboardState({ + id: newSavedObjectId, + }); const dashboardContainerReady$ = new Subject(); const untilDashboardReady = () => @@ -908,13 +892,10 @@ export class DashboardContainer }; public async getPanelTitles(): Promise { - const { - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); const titles: string[] = []; for (const [id, panel] of Object.entries(this.getInput().panels)) { const title = await (async () => { - if (reactEmbeddableRegistryHasKey(panel.type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(panel.type)) { const child = this.children$.value[id]; return apiPublishesPanelTitle(child) ? getPanelTitle(child) : ''; } @@ -1039,12 +1020,9 @@ export class DashboardContainer }; public removePanel(id: string) { - const { - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); const type = this.getInput().panels[id]?.type; this.removeEmbeddable(id); - if (reactEmbeddableRegistryHasKey(type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(type)) { const { [id]: childToRemove, ...otherChildren } = this.children$.value; this.children$.next(otherChildren); } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx index 1a87659370f25..ecccc49a60f12 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx @@ -29,7 +29,7 @@ import { DEFAULT_DASHBOARD_INPUT } from '../../dashboard_constants'; import { LoadDashboardReturn, SavedDashboardInput, -} from '../../services/dashboard_content_management/types'; +} from '../../services/dashboard_content_management_service/types'; import type { DashboardContainer } from './dashboard_container'; export type DashboardContainerFactory = EmbeddableFactory< diff --git a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.test.tsx b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.test.tsx index 6248800cf3740..153324f5a2bf3 100644 --- a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.test.tsx +++ b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.test.tsx @@ -7,22 +7,22 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; +import { setStubKibanaServices } from '@kbn/embeddable-plugin/public/mocks'; +import { NotFoundPrompt } from '@kbn/shared-ux-prompt-not-found'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ReactWrapper } from 'enzyme'; +import React from 'react'; import { act } from 'react-dom/test-utils'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { NotFoundPrompt } from '@kbn/shared-ux-prompt-not-found'; -import { setStubKibanaServices } from '@kbn/embeddable-plugin/public/mocks'; +import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common'; +import { setStubKibanaServices as setPresentationPanelMocks } from '@kbn/presentation-panel-plugin/public/mocks'; +import { BehaviorSubject } from 'rxjs'; import { DashboardContainerFactory } from '..'; import { DASHBOARD_CONTAINER_TYPE } from '../..'; -import { DashboardRenderer } from './dashboard_renderer'; -import { pluginServices } from '../../services/plugin_services'; -import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common'; +import { embeddableService } from '../../services/kibana_services'; import { DashboardContainer } from '../embeddable/dashboard_container'; import { DashboardCreationOptions } from '../embeddable/dashboard_container_factory'; -import { setStubKibanaServices as setPresentationPanelMocks } from '@kbn/presentation-panel-plugin/public/mocks'; -import { BehaviorSubject } from 'rxjs'; +import { DashboardRenderer } from './dashboard_renderer'; describe('dashboard renderer', () => { let mockDashboardContainer: DashboardContainer; @@ -39,9 +39,7 @@ describe('dashboard renderer', () => { mockDashboardFactory = { create: jest.fn().mockReturnValue(mockDashboardContainer), } as unknown as DashboardContainerFactory; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockDashboardFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockDashboardFactory); setPresentationPanelMocks(); }); @@ -49,9 +47,7 @@ describe('dashboard renderer', () => { await act(async () => { mountWithIntl(); }); - expect(pluginServices.getServices().embeddable.getEmbeddableFactory).toHaveBeenCalledWith( - DASHBOARD_CONTAINER_TYPE - ); + expect(embeddableService.getEmbeddableFactory).toHaveBeenCalledWith(DASHBOARD_CONTAINER_TYPE); expect(mockDashboardFactory.create).toHaveBeenCalled(); }); @@ -109,9 +105,7 @@ describe('dashboard renderer', () => { mockDashboardFactory = { create: jest.fn().mockReturnValue(mockErrorEmbeddable), } as unknown as DashboardContainerFactory; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockDashboardFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockDashboardFactory); let wrapper: ReactWrapper; await act(async () => { @@ -133,9 +127,7 @@ describe('dashboard renderer', () => { const mockErrorFactory = { create: jest.fn().mockReturnValue(mockErrorEmbeddable), } as unknown as DashboardContainerFactory; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockErrorFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockErrorFactory); // render the dashboard - it should run into an error and render the error embeddable. let wrapper: ReactWrapper; @@ -156,9 +148,7 @@ describe('dashboard renderer', () => { const mockSuccessFactory = { create: jest.fn().mockReturnValue(mockSuccessEmbeddable), } as unknown as DashboardContainerFactory; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockSuccessFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockSuccessFactory); // update the saved object id to trigger another dashboard load. await act(async () => { @@ -187,9 +177,7 @@ describe('dashboard renderer', () => { const mockErrorFactory = { create: jest.fn().mockReturnValue(mockErrorEmbeddable), } as unknown as DashboardContainerFactory; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockErrorFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockErrorFactory); // render the dashboard - it should run into an error and render the error embeddable. let wrapper: ReactWrapper; @@ -252,9 +240,7 @@ describe('dashboard renderer', () => { const mockSuccessFactory = { create: jest.fn().mockReturnValue(mockSuccessEmbeddable), } as unknown as DashboardContainerFactory; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockSuccessFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockSuccessFactory); let wrapper: ReactWrapper; await act(async () => { @@ -279,9 +265,7 @@ describe('dashboard renderer', () => { const mockUseMarginFalseFactory = { create: jest.fn().mockReturnValue(mockUseMarginFalseEmbeddable), } as unknown as DashboardContainerFactory; - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockUseMarginFalseFactory); + embeddableService.getEmbeddableFactory = jest.fn().mockReturnValue(mockUseMarginFalseFactory); let wrapper: ReactWrapper; await act(async () => { diff --git a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx index 1222b3433877a..e8ef4faef724d 100644 --- a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx +++ b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx @@ -17,11 +17,13 @@ import { v4 as uuidv4 } from 'uuid'; import { EuiLoadingElastic, EuiLoadingSpinner } from '@elastic/eui'; import { ErrorEmbeddable, isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common'; - -import { LocatorPublic } from '@kbn/share-plugin/common'; import { useStateFromPublishingSubject } from '@kbn/presentation-publishing'; +import { LocatorPublic } from '@kbn/share-plugin/common'; + import { DASHBOARD_CONTAINER_TYPE } from '..'; import { DashboardContainerInput } from '../../../common'; +import { DashboardApi } from '../../dashboard_api/types'; +import { embeddableService, screenshotModeService } from '../../services/kibana_services'; import type { DashboardContainer } from '../embeddable/dashboard_container'; import { DashboardContainerFactory, @@ -30,8 +32,6 @@ import { } from '../embeddable/dashboard_container_factory'; import { DashboardLocatorParams, DashboardRedirect } from '../types'; import { Dashboard404Page } from './dashboard_404'; -import { DashboardApi } from '../../dashboard_api/types'; -import { pluginServices } from '../../services/plugin_services'; export interface DashboardRendererProps { onApiAvailable?: (api: DashboardApi) => void; @@ -57,8 +57,6 @@ export function DashboardRenderer({ const [fatalError, setFatalError] = useState(); const [dashboardMissing, setDashboardMissing] = useState(false); - const { embeddable, screenshotMode } = pluginServices.getServices(); - const id = useMemo(() => uuidv4(), []); useEffect(() => { @@ -93,7 +91,7 @@ export function DashboardRenderer({ (async () => { const creationOptions = await getCreationOptions?.(); - const dashboardFactory = embeddable.getEmbeddableFactory( + const dashboardFactory = embeddableService.getEmbeddableFactory( DASHBOARD_CONTAINER_TYPE ) as DashboardContainerFactory & { create: DashboardContainerFactoryDefinition['create']; @@ -141,7 +139,7 @@ export function DashboardRenderer({ const viewportClasses = classNames( 'dashboardViewport', - { 'dashboardViewport--screenshotMode': screenshotMode }, + { 'dashboardViewport--screenshotMode': screenshotModeService.isScreenshotMode() }, { 'dashboardViewport--loading': loading } ); diff --git a/src/plugins/dashboard/public/dashboard_container/external_api/lazy_dashboard_renderer.tsx b/src/plugins/dashboard/public/dashboard_container/external_api/lazy_dashboard_renderer.tsx index 56c6e5f82ac15..d8e3c6668df38 100644 --- a/src/plugins/dashboard/public/dashboard_container/external_api/lazy_dashboard_renderer.tsx +++ b/src/plugins/dashboard/public/dashboard_container/external_api/lazy_dashboard_renderer.tsx @@ -10,9 +10,13 @@ import React from 'react'; import { dynamic } from '@kbn/shared-ux-utility'; import type { DashboardRendererProps } from './dashboard_renderer'; +import { untilPluginStartServicesReady } from '../../services/kibana_services'; const Component = dynamic(async () => { - const { DashboardRenderer } = await import('./dashboard_renderer'); + const [{ DashboardRenderer }] = await Promise.all([ + import('./dashboard_renderer'), + untilPluginStartServicesReady(), + ]); return { default: DashboardRenderer, }; diff --git a/src/plugins/dashboard/public/dashboard_container/index.ts b/src/plugins/dashboard/public/dashboard_container/index.ts index 815823f74164d..d8103f1e3f9bc 100644 --- a/src/plugins/dashboard/public/dashboard_container/index.ts +++ b/src/plugins/dashboard/public/dashboard_container/index.ts @@ -8,7 +8,7 @@ */ import { LATEST_VERSION } from '../../common/content_management'; -import { convertNumberToDashboardVersion } from '../services/dashboard_content_management/lib/dashboard_versioning'; +import { convertNumberToDashboardVersion } from '../services/dashboard_content_management_service/lib/dashboard_versioning'; export const DASHBOARD_CONTAINER_TYPE = 'dashboard'; diff --git a/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_functions.ts b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_functions.ts index 72026f2ccd842..2803d9be0e32d 100644 --- a/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_functions.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_functions.ts @@ -7,10 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { compareFilters, COMPARE_ALL_OPTIONS, isFilterPinned } from '@kbn/es-query'; import fastIsEqual from 'fast-deep-equal'; + +import { COMPARE_ALL_OPTIONS, compareFilters, isFilterPinned } from '@kbn/es-query'; + import { DashboardContainerInput } from '../../../../common'; -import { pluginServices } from '../../../services/plugin_services'; +import { embeddableService } from '../../../services/kibana_services'; import { DashboardContainer } from '../../embeddable/dashboard_container'; import { DashboardContainerInputWithoutId } from '../../types'; import { areTimesEqual, getPanelLayoutsAreEqual } from './dashboard_diffing_utils'; @@ -75,11 +77,8 @@ export const unsavedChangesDiffingFunctions: DashboardDiffFunctions = { const explicitInputComparePromises = Object.values(currentValue ?? {}).map( (panel) => new Promise((resolve, reject) => { - const { - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); const embeddableId = panel.explicitInput.id; - if (!embeddableId || reactEmbeddableRegistryHasKey(panel.type)) { + if (!embeddableId || embeddableService.reactEmbeddableRegistryHasKey(panel.type)) { // if this is a new style embeddable, it will handle its own diffing. reject(); return; diff --git a/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.ts b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.ts index b0337ccbb0d38..1bcba571dc24f 100644 --- a/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.ts @@ -13,11 +13,13 @@ import { combineLatest, debounceTime, skipWhile, startWith, switchMap } from 'rx import { DashboardContainer, DashboardCreationOptions } from '../..'; import { DashboardContainerInput } from '../../../../common'; import { CHANGE_CHECK_DEBOUNCE } from '../../../dashboard_constants'; -import { pluginServices } from '../../../services/plugin_services'; +import { + PANELS_CONTROL_GROUP_KEY, + getDashboardBackupService, +} from '../../../services/dashboard_backup_service'; import { UnsavedPanelState } from '../../types'; import { dashboardContainerReducers } from '../dashboard_container_reducers'; import { isKeyEqualAsync, unsavedChangesDiffingFunctions } from './dashboard_diffing_functions'; -import { PANELS_CONTROL_GROUP_KEY } from '../../../services/dashboard_backup/dashboard_backup_service'; /** * An array of reducers which cannot cause unsaved changes. Unsaved changes only compares the explicit input @@ -188,10 +190,9 @@ function backupUnsavedChanges( dashboardChanges: Partial, reactEmbeddableChanges: UnsavedPanelState ) { - const { dashboardBackup } = pluginServices.getServices(); const dashboardStateToBackup = omit(dashboardChanges, keysToOmitFromSessionStorage); - dashboardBackup.setState( + getDashboardBackupService().setState( this.getDashboardSavedObjectId(), { ...dashboardStateToBackup, diff --git a/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx index de1d872f081a8..f3dac2e9bb624 100644 --- a/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { + EUI_MODAL_CANCEL_BUTTON, EuiButton, EuiButtonEmpty, EuiFocusTrap, @@ -19,12 +20,11 @@ import { EuiModalHeaderTitle, EuiOutsideClickDetector, EuiText, - EUI_MODAL_CANCEL_BUTTON, } from '@elastic/eui'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount'; -import { pluginServices } from '../services/plugin_services'; +import { coreServices } from '../services/kibana_services'; import { createConfirmStrings, resetConfirmStrings } from './_dashboard_listing_strings'; export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep'; @@ -33,21 +33,19 @@ export const confirmDiscardUnsavedChanges = ( discardCallback: () => void, viewMode: ViewMode = ViewMode.EDIT // we want to show the danger modal on the listing page ) => { - const { - overlays: { openConfirm }, - } = pluginServices.getServices(); - - openConfirm(resetConfirmStrings.getResetSubtitle(viewMode), { - confirmButtonText: resetConfirmStrings.getResetConfirmButtonText(), - buttonColor: viewMode === ViewMode.EDIT ? 'danger' : 'primary', - maxWidth: 500, - defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON, - title: resetConfirmStrings.getResetTitle(), - }).then((isConfirmed) => { - if (isConfirmed) { - discardCallback(); - } - }); + coreServices.overlays + .openConfirm(resetConfirmStrings.getResetSubtitle(viewMode), { + confirmButtonText: resetConfirmStrings.getResetConfirmButtonText(), + buttonColor: viewMode === ViewMode.EDIT ? 'danger' : 'primary', + maxWidth: 500, + defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON, + title: resetConfirmStrings.getResetTitle(), + }) + .then((isConfirmed) => { + if (isConfirmed) { + discardCallback(); + } + }); }; export const confirmCreateWithUnsaved = ( @@ -57,13 +55,7 @@ export const confirmCreateWithUnsaved = ( const titleId = 'confirmDiscardOrKeepTitle'; const descriptionId = 'confirmDiscardOrKeepDescription'; - const { - analytics, - settings: { i18n, theme }, - overlays: { openModal }, - } = pluginServices.getServices(); - - const session = openModal( + const session = coreServices.overlays.openModal( toMountPoint( , - { analytics, i18n, theme } + { analytics: coreServices.analytics, i18n: coreServices.i18n, theme: coreServices.theme } ), { 'data-test-subj': 'dashboardCreateConfirmModal', diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx index c1aabad7eb23f..e17433709b1f1 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx @@ -7,22 +7,22 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { ComponentType, ReactWrapper, mount } from 'enzyme'; import React, { PropsWithChildren } from 'react'; import { act } from 'react-dom/test-utils'; -import { mount, ReactWrapper, ComponentType } from 'enzyme'; -import { I18nProvider } from '@kbn/i18n-react'; - -import { pluginServices } from '../services/plugin_services'; -import { DashboardListing } from './dashboard_listing'; +import { I18nProvider } from '@kbn/i18n-react'; /** * Mock Table List view. This dashboard component is a wrapper around the shared UX table List view. We * need to ensure we're passing down the correct props, but the table list view itself doesn't need to be rendered * in our tests because it is covered in its package. */ import { TableListView } from '@kbn/content-management-table-list-view'; + +import { DashboardListing } from './dashboard_listing'; import { DashboardListingProps } from './types'; -// import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view'; +import { coreServices } from '../services/kibana_services'; + jest.mock('@kbn/content-management-table-list-view-table', () => { const originalModule = jest.requireActual('@kbn/content-management-table-list-view-table'); return { @@ -65,7 +65,7 @@ function mountWith({ props: incomingProps }: { props?: Partial { - pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + (coreServices.application.capabilities as any).dashboard.showWriteControls = false; let component: ReactWrapper; @@ -80,7 +80,7 @@ test('initial filter is passed through', async () => { }); test('when showWriteControls is true, table list view is passed editing functions', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + (coreServices.application.capabilities as any).dashboard.showWriteControls = true; let component: ReactWrapper; @@ -99,7 +99,7 @@ test('when showWriteControls is true, table list view is passed editing function }); test('when showWriteControls is false, table list view is not passed editing functions', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + (coreServices.application.capabilities as any).dashboard.showWriteControls = false; let component: ReactWrapper; diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 96449ed7f3ec8..f6072169e5bda 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -7,26 +7,23 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; import React, { useMemo } from 'react'; +import { FavoritesClient } from '@kbn/content-management-favorites-public'; import { TableListView } from '@kbn/content-management-table-list-view'; -import { - type TableListViewKibanaDependencies, - TableListViewKibanaProvider, -} from '@kbn/content-management-table-list-view-table'; - +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view-table'; +import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; -import { pluginServices } from '../services/plugin_services'; - +import { DASHBOARD_APP_ID, DASHBOARD_CONTENT_ID } from '../dashboard_constants'; +import { + coreServices, + savedObjectsTaggingService, + usageCollectionService, +} from '../services/kibana_services'; import { DashboardUnsavedListing } from './dashboard_unsaved_listing'; import { useDashboardListingTable } from './hooks/use_dashboard_listing_table'; -import { - DashboardListingProps, - DashboardSavedObjectUserContent, - TableListViewApplicationService, -} from './types'; +import { DashboardListingProps, DashboardSavedObjectUserContent } from './types'; export const DashboardListing = ({ children, @@ -35,59 +32,38 @@ export const DashboardListing = ({ getDashboardUrl, useSessionStorageIntegration, }: DashboardListingProps) => { - const { - analytics, - application, - notifications, - overlays, - http, - i18n, - chrome: { theme }, - savedObjectsTagging, - coreContext: { executionContext }, - userProfile, - dashboardContentInsights: { contentInsightsClient }, - dashboardFavorites, - } = pluginServices.getServices(); - - useExecutionContext(executionContext, { + useExecutionContext(coreServices.executionContext, { type: 'application', page: 'list', }); - const { unsavedDashboardIds, refreshUnsavedDashboards, tableListViewTableProps } = - useDashboardListingTable({ - goToDashboard, - getDashboardUrl, - useSessionStorageIntegration, - initialFilter, - }); + const { + unsavedDashboardIds, + refreshUnsavedDashboards, + tableListViewTableProps, + contentInsightsClient, + } = useDashboardListingTable({ + goToDashboard, + getDashboardUrl, + useSessionStorageIntegration, + initialFilter, + }); - const savedObjectsTaggingFakePlugin = useMemo(() => { - return savedObjectsTagging.hasApi // TODO: clean up this logic once https://github.com/elastic/kibana/issues/140433 is resolved - ? ({ - ui: savedObjectsTagging, - } as TableListViewKibanaDependencies['savedObjectsTagging']) - : undefined; - }, [savedObjectsTagging]); + const dashboardFavoritesClient = useMemo(() => { + return new FavoritesClient(DASHBOARD_APP_ID, DASHBOARD_CONTENT_ID, { + http: coreServices.http, + usageCollection: usageCollectionService, + }); + }, []); return ( diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx index 92d108231b4fc..a5661238f9ad1 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx @@ -7,18 +7,18 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { ComponentType, ReactWrapper, mount } from 'enzyme'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import { mount, ReactWrapper, ComponentType } from 'enzyme'; import { I18nProvider } from '@kbn/i18n-react'; +import { coreServices } from '../services/kibana_services'; +import { confirmDiscardUnsavedChanges } from './confirm_overlays'; import { DashboardListingEmptyPrompt, DashboardListingEmptyPromptProps, } from './dashboard_listing_empty_prompt'; -import { pluginServices } from '../services/plugin_services'; -import { confirmDiscardUnsavedChanges } from './confirm_overlays'; jest.mock('./confirm_overlays', () => { const originalModule = jest.requireActual('./confirm_overlays'); @@ -56,7 +56,7 @@ function mountWith({ } test('renders readonly empty prompt when showWriteControls is off', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + (coreServices.application.capabilities as any).dashboard.showWriteControls = false; let component: ReactWrapper; await act(async () => { @@ -68,7 +68,7 @@ test('renders readonly empty prompt when showWriteControls is off', async () => }); test('renders empty prompt with link when showWriteControls is on', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + (coreServices.application.capabilities as any).dashboard.showWriteControls = true; let component: ReactWrapper; await act(async () => { @@ -80,7 +80,7 @@ test('renders empty prompt with link when showWriteControls is on', async () => }); test('renders disabled action button when disableCreateDashboardButton is true', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + (coreServices.application.capabilities as any).dashboard.showWriteControls = true; let component: ReactWrapper; await act(async () => { @@ -95,7 +95,7 @@ test('renders disabled action button when disableCreateDashboardButton is true', }); test('renders continue button when no dashboards exist but one is in progress', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + (coreServices.application.capabilities as any).dashboard.showWriteControls = true; let component: ReactWrapper; let props: DashboardListingEmptyPromptProps; await act(async () => { @@ -114,7 +114,7 @@ test('renders continue button when no dashboards exist but one is in progress', }); test('renders discard button when no dashboards exist but one is in progress', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + (coreServices.application.capabilities as any).dashboard.showWriteControls = true; let component: ReactWrapper; await act(async () => { ({ component } = mountWith({ diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx index dfb52d579c397..3ddaca1413dc8 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx @@ -8,24 +8,28 @@ */ import { - EuiLink, EuiButton, - EuiFlexItem, - EuiFlexGroup, EuiButtonEmpty, EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiLink, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useCallback, useMemo } from 'react'; import { - noItemsStrings, - getNewDashboardTitle, + DASHBOARD_PANELS_UNSAVED_ID, + getDashboardBackupService, +} from '../services/dashboard_backup_service'; +import { coreServices } from '../services/kibana_services'; +import { getDashboardCapabilities } from '../utils/get_dashboard_capabilities'; +import { dashboardUnsavedListingStrings, + getNewDashboardTitle, + noItemsStrings, } from './_dashboard_listing_strings'; -import { pluginServices } from '../services/plugin_services'; import { confirmDiscardUnsavedChanges } from './confirm_overlays'; -import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_backup/dashboard_backup_service'; import { DashboardListingProps } from './types'; export interface DashboardListingEmptyPromptProps { @@ -45,12 +49,6 @@ export const DashboardListingEmptyPrompt = ({ createItem, disableCreateDashboardButton, }: DashboardListingEmptyPromptProps) => { - const { - application, - dashboardBackup, - dashboardCapabilities: { showWriteControls }, - } = pluginServices.getServices(); - const isEditingFirstDashboard = useMemo( () => useSessionStorageIntegration && unsavedDashboardIds.length === 1, [unsavedDashboardIds.length, useSessionStorageIntegration] @@ -78,8 +76,9 @@ export const DashboardListingEmptyPrompt = ({ color="danger" onClick={() => confirmDiscardUnsavedChanges(() => { - dashboardBackup.clearState(DASHBOARD_PANELS_UNSAVED_ID); - setUnsavedDashboardIds(dashboardBackup.getDashboardIdsWithUnsavedChanges()); + const dashboardBackupService = getDashboardBackupService(); + dashboardBackupService.clearState(DASHBOARD_PANELS_UNSAVED_ID); + setUnsavedDashboardIds(dashboardBackupService.getDashboardIdsWithUnsavedChanges()); }) } data-test-subj="discardDashboardPromptButton" @@ -106,12 +105,11 @@ export const DashboardListingEmptyPrompt = ({ isEditingFirstDashboard, createItem, disableCreateDashboardButton, - dashboardBackup, goToDashboard, setUnsavedDashboardIds, ]); - if (!showWriteControls) { + if (!getDashboardCapabilities().showWriteControls) { return ( - application.navigateToApp('home', { + coreServices.application.navigateToApp('home', { path: '#/tutorial_directory/sampleData', }) } diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx index 5935b4d861a47..96d9025f822ff 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx @@ -7,26 +7,19 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; -import React, { useMemo } from 'react'; +import React from 'react'; import { - type TableListViewKibanaDependencies, TableListViewKibanaProvider, TableListViewTable, } from '@kbn/content-management-table-list-view-table'; - +import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; -import { pluginServices } from '../services/plugin_services'; - +import { coreServices, savedObjectsTaggingService } from '../services/kibana_services'; import { DashboardUnsavedListing } from './dashboard_unsaved_listing'; import { useDashboardListingTable } from './hooks/use_dashboard_listing_table'; -import { - DashboardListingProps, - DashboardSavedObjectUserContent, - TableListViewApplicationService, -} from './types'; +import { DashboardListingProps, DashboardSavedObjectUserContent } from './types'; export const DashboardListingTable = ({ disableCreateDashboardButton, @@ -37,21 +30,7 @@ export const DashboardListingTable = ({ urlStateEnabled, showCreateDashboardButton = true, }: DashboardListingProps) => { - const { - analytics, - application, - notifications, - overlays, - http, - i18n, - savedObjectsTagging, - coreContext: { executionContext }, - chrome: { theme }, - userProfile, - dashboardContentInsights: { contentInsightsClient }, - } = pluginServices.getServices(); - - useExecutionContext(executionContext, { + useExecutionContext(coreServices.executionContext, { type: 'application', page: 'list', }); @@ -60,6 +39,7 @@ export const DashboardListingTable = ({ unsavedDashboardIds, refreshUnsavedDashboards, tableListViewTableProps: { title: tableCaption, ...tableListViewTable }, + contentInsightsClient, } = useDashboardListingTable({ disableCreateDashboardButton, goToDashboard, @@ -70,35 +50,11 @@ export const DashboardListingTable = ({ showCreateDashboardButton, }); - const savedObjectsTaggingFakePlugin = useMemo( - () => - savedObjectsTagging.hasApi // TODO: clean up this logic once https://github.com/elastic/kibana/issues/140433 is resolved - ? ({ - ui: savedObjectsTagging, - } as TableListViewKibanaDependencies['savedObjectsTagging']) - : undefined, - [savedObjectsTagging] - ); - - const core = useMemo( - () => ({ - analytics, - application: application as TableListViewApplicationService, - notifications, - overlays, - http, - i18n, - theme, - userProfile, - }), - [application, notifications, overlays, http, analytics, i18n, theme, userProfile] - ); - return ( diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.test.tsx index e777cc12258ee..74b538d87c207 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.test.tsx @@ -7,17 +7,21 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { ComponentType, mount } from 'enzyme'; import React from 'react'; -import { mount, ComponentType } from 'enzyme'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { I18nProvider } from '@kbn/i18n-react'; import { waitFor } from '@testing-library/react'; -import { findTestSubject } from '@elastic/eui/lib/test'; -import { pluginServices } from '../services/plugin_services'; +import { + DASHBOARD_PANELS_UNSAVED_ID, + getDashboardBackupService, +} from '../services/dashboard_backup_service'; +import { getDashboardContentManagementService } from '../services/dashboard_content_management_service'; +import { coreServices } from '../services/kibana_services'; import { DashboardUnsavedListing, DashboardUnsavedListingProps } from './dashboard_unsaved_listing'; -import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_backup/dashboard_backup_service'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; const makeDefaultProps = (): DashboardUnsavedListingProps => ({ goToDashboard: jest.fn(), @@ -39,12 +43,13 @@ function mountWith({ props: incomingProps }: { props?: Partial { + const dashboardBackupService = getDashboardBackupService(); + const dashboardContentManagementService = getDashboardContentManagementService(); + it('Gets information for each unsaved dashboard', async () => { mountWith({}); await waitFor(() => { - expect( - pluginServices.getServices().dashboardContentManagement.findDashboards.findByIds - ).toHaveBeenCalledTimes(1); + expect(dashboardContentManagementService.findDashboards.findByIds).toHaveBeenCalledTimes(1); }); }); @@ -53,9 +58,9 @@ describe('Unsaved listing', () => { props.unsavedDashboardIds = ['dashboardUnsavedOne', DASHBOARD_PANELS_UNSAVED_ID]; mountWith({ props }); await waitFor(() => { - expect( - pluginServices.getServices().dashboardContentManagement.findDashboards.findByIds - ).toHaveBeenCalledWith(['dashboardUnsavedOne']); + expect(dashboardContentManagementService.findDashboards.findByIds).toHaveBeenCalledWith([ + 'dashboardUnsavedOne', + ]); }); }); @@ -94,17 +99,13 @@ describe('Unsaved listing', () => { getDiscardButton().simulate('click'); waitFor(() => { component.update(); - expect(pluginServices.getServices().overlays.openConfirm).toHaveBeenCalled(); - expect(pluginServices.getServices().dashboardBackup.clearState).toHaveBeenCalledWith( - 'dashboardUnsavedOne' - ); + expect(coreServices.overlays.openConfirm).toHaveBeenCalled(); + expect(dashboardBackupService.clearState).toHaveBeenCalledWith('dashboardUnsavedOne'); }); }); it('removes unsaved changes from any dashboard which errors on fetch', async () => { - ( - pluginServices.getServices().dashboardContentManagement.findDashboards.findByIds as jest.Mock - ).mockResolvedValue([ + (dashboardContentManagementService.findDashboards.findByIds as jest.Mock).mockResolvedValue([ { id: 'failCase1', status: 'error', @@ -129,17 +130,11 @@ describe('Unsaved listing', () => { const { component } = mountWith({ props }); waitFor(() => { component.update(); - expect(pluginServices.getServices().dashboardBackup.clearState).toHaveBeenCalledWith( - 'failCase1' - ); - expect(pluginServices.getServices().dashboardBackup.clearState).toHaveBeenCalledWith( - 'failCase2' - ); + expect(dashboardBackupService.clearState).toHaveBeenCalledWith('failCase1'); + expect(dashboardBackupService.clearState).toHaveBeenCalledWith('failCase2'); // clearing panels from dashboard with errors should cause getDashboardIdsWithUnsavedChanges to be called again. - expect( - pluginServices.getServices().dashboardBackup.getDashboardIdsWithUnsavedChanges - ).toHaveBeenCalledTimes(2); + expect(dashboardBackupService.getDashboardIdsWithUnsavedChanges).toHaveBeenCalledTimes(2); }); }); }); diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.tsx index 02f8cb483e717..0e23583801309 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.tsx @@ -16,15 +16,18 @@ import { EuiSpacer, EuiTitle, } from '@elastic/eui'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { pluginServices } from '../services/plugin_services'; -import { confirmDiscardUnsavedChanges } from './confirm_overlays'; import { DashboardAttributes } from '../../common/content_management'; +import { + DASHBOARD_PANELS_UNSAVED_ID, + getDashboardBackupService, +} from '../services/dashboard_backup_service'; +import { getDashboardContentManagementService } from '../services/dashboard_content_management_service'; import { dashboardUnsavedListingStrings, getNewDashboardTitle } from './_dashboard_listing_strings'; -import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_backup/dashboard_backup_service'; +import { confirmDiscardUnsavedChanges } from './confirm_overlays'; const DashboardUnsavedItem = ({ id, @@ -116,12 +119,8 @@ export const DashboardUnsavedListing = ({ unsavedDashboardIds, refreshUnsavedDashboards, }: DashboardUnsavedListingProps) => { - const { - dashboardBackup, - dashboardContentManagement: { findDashboards }, - } = pluginServices.getServices(); - const [items, setItems] = useState({}); + const dashboardBackupService = useMemo(() => getDashboardBackupService(), []); const onOpen = useCallback( (id?: string) => { @@ -133,11 +132,11 @@ export const DashboardUnsavedListing = ({ const onDiscard = useCallback( (id?: string) => { confirmDiscardUnsavedChanges(() => { - dashboardBackup.clearState(id); + dashboardBackupService.clearState(id); refreshUnsavedDashboards(); }); }, - [refreshUnsavedDashboards, dashboardBackup] + [dashboardBackupService, refreshUnsavedDashboards] ); useEffect(() => { @@ -148,37 +147,39 @@ export const DashboardUnsavedListing = ({ const existingDashboardsWithUnsavedChanges = unsavedDashboardIds.filter( (id) => id !== DASHBOARD_PANELS_UNSAVED_ID ); - findDashboards.findByIds(existingDashboardsWithUnsavedChanges).then((results) => { - const dashboardMap = {}; - if (canceled) { - return; - } - let hasError = false; - const newItems = results.reduce((map, result) => { - if (result.status === 'error') { - hasError = true; - if (result.error.statusCode === 404) { - // Save object not found error - dashboardBackup.clearState(result.id); + getDashboardContentManagementService() + .findDashboards.findByIds(existingDashboardsWithUnsavedChanges) + .then((results) => { + const dashboardMap = {}; + if (canceled) { + return; + } + let hasError = false; + const newItems = results.reduce((map, result) => { + if (result.status === 'error') { + hasError = true; + if (result.error.statusCode === 404) { + // Save object not found error + dashboardBackupService.clearState(result.id); + } + return map; } - return map; + return { + ...map, + [result.id || DASHBOARD_PANELS_UNSAVED_ID]: result.attributes, + }; + }, dashboardMap); + if (hasError) { + refreshUnsavedDashboards(); + return; } - return { - ...map, - [result.id || DASHBOARD_PANELS_UNSAVED_ID]: result.attributes, - }; - }, dashboardMap); - if (hasError) { - refreshUnsavedDashboards(); - return; - } - setItems(newItems); - }); + setItems(newItems); + }); return () => { canceled = true; }; - }, [refreshUnsavedDashboards, dashboardBackup, unsavedDashboardIds, findDashboards]); + }, [dashboardBackupService, refreshUnsavedDashboards, unsavedDashboardIds]); return unsavedDashboardIds.length === 0 ? null : ( <> diff --git a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx index b6f5a736e079f..61b7bf402bc13 100644 --- a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx @@ -9,10 +9,13 @@ import { act, renderHook } from '@testing-library/react-hooks'; -import { useDashboardListingTable } from './use_dashboard_listing_table'; -import { pluginServices } from '../../services/plugin_services'; +import { getDashboardBackupService } from '../../services/dashboard_backup_service'; +import { getDashboardContentManagementService } from '../../services/dashboard_content_management_service'; +import { coreServices } from '../../services/kibana_services'; import { confirmCreateWithUnsaved } from '../confirm_overlays'; import { DashboardSavedObjectUserContent } from '../types'; +import { useDashboardListingTable } from './use_dashboard_listing_table'; + const clearStateMock = jest.fn(); const getDashboardUrl = jest.fn(); const goToDashboard = jest.fn(); @@ -26,7 +29,6 @@ const getUiSettingsMock = jest.fn().mockImplementation((key) => { } return null; }); -const getPluginServices = pluginServices.getServices(); jest.mock('@kbn/ebt-tools', () => ({ reportPerformanceMetricEvent: jest.fn(), @@ -45,20 +47,20 @@ jest.mock('../_dashboard_listing_strings', () => ({ })); describe('useDashboardListingTable', () => { + const dashboardBackupService = getDashboardBackupService(); + const dashboardContentManagementService = getDashboardContentManagementService(); + beforeEach(() => { jest.clearAllMocks(); - getPluginServices.dashboardBackup.dashboardHasUnsavedEdits = jest.fn().mockReturnValue(true); + dashboardBackupService.dashboardHasUnsavedEdits = jest.fn().mockReturnValue(true); - getPluginServices.dashboardBackup.getDashboardIdsWithUnsavedChanges = jest - .fn() - .mockReturnValue([]); + dashboardBackupService.getDashboardIdsWithUnsavedChanges = jest.fn().mockReturnValue([]); - getPluginServices.dashboardBackup.clearState = clearStateMock; - getPluginServices.dashboardCapabilities.showWriteControls = true; - getPluginServices.dashboardContentManagement.deleteDashboards = deleteDashboards; - getPluginServices.settings.uiSettings.get = getUiSettingsMock; - getPluginServices.notifications.toasts.addError = jest.fn(); + dashboardBackupService.clearState = clearStateMock; + dashboardContentManagementService.deleteDashboards = deleteDashboards; + coreServices.uiSettings.get = getUiSettingsMock; + coreServices.notifications.toasts.addError = jest.fn(); }); test('should return the correct initial hasInitialFetchReturned state', () => { @@ -232,8 +234,12 @@ describe('useDashboardListingTable', () => { }); test('createItem should be undefined when showWriteControls equals false', () => { - getPluginServices.dashboardCapabilities.showWriteControls = false; - + coreServices.application.capabilities = { + ...coreServices.application.capabilities, + dashboard: { + showWriteControls: false, + }, + }; const { result } = renderHook(() => useDashboardListingTable({ getDashboardUrl, @@ -245,7 +251,8 @@ describe('useDashboardListingTable', () => { }); test('deleteItems should be undefined when showWriteControls equals false', () => { - getPluginServices.dashboardCapabilities.showWriteControls = false; + (coreServices.application.capabilities as any).dashboard.showWriteControls = false; + const { result } = renderHook(() => useDashboardListingTable({ getDashboardUrl, @@ -257,7 +264,8 @@ describe('useDashboardListingTable', () => { }); test('editItem should be undefined when showWriteControls equals false', () => { - getPluginServices.dashboardCapabilities.showWriteControls = false; + (coreServices.application.capabilities as any).dashboard.showWriteControls = false; + const { result } = renderHook(() => useDashboardListingTable({ getDashboardUrl, diff --git a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx index 81986a7c37a2d..31bfa88120a5e 100644 --- a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx @@ -7,29 +7,34 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { useCallback, useState, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; -import type { SavedObjectsFindOptionsReference } from '@kbn/core/public'; import { OpenContentEditorParams } from '@kbn/content-management-content-editor'; +import { ContentInsightsClient } from '@kbn/content-management-content-insights-public'; import { TableListViewTableProps } from '@kbn/content-management-table-list-view-table'; +import type { SavedObjectsFindOptionsReference } from '@kbn/core/public'; +import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { DashboardContainerInput } from '../../../common'; +import { DashboardItem } from '../../../common/content_management'; import { DASHBOARD_CONTENT_ID, SAVED_OBJECT_DELETE_TIME, SAVED_OBJECT_LOADED_TIME, } from '../../dashboard_constants'; +import { getDashboardBackupService } from '../../services/dashboard_backup_service'; +import { getDashboardContentManagementService } from '../../services/dashboard_content_management_service'; +import { getDashboardRecentlyAccessedService } from '../../services/dashboard_recently_accessed_service'; +import { coreServices } from '../../services/kibana_services'; +import { getDashboardCapabilities } from '../../utils/get_dashboard_capabilities'; import { dashboardListingErrorStrings, dashboardListingTableStrings, } from '../_dashboard_listing_strings'; -import { DashboardContainerInput } from '../../../common'; -import { DashboardSavedObjectUserContent } from '../types'; import { confirmCreateWithUnsaved } from '../confirm_overlays'; -import { pluginServices } from '../../services/plugin_services'; -import { DashboardItem } from '../../../common/content_management'; import { DashboardListingEmptyPrompt } from '../dashboard_listing_empty_prompt'; +import { DashboardSavedObjectUserContent } from '../types'; type GetDetailViewLink = TableListViewTableProps['getDetailViewLink']; @@ -67,6 +72,7 @@ interface UseDashboardListingTableReturnType { refreshUnsavedDashboards: () => void; tableListViewTableProps: DashboardListingViewTableProps; unsavedDashboardIds: string[]; + contentInsightsClient: ContentInsightsClient; } export const useDashboardListingTable = ({ @@ -90,51 +96,44 @@ export const useDashboardListingTable = ({ useSessionStorageIntegration?: boolean; showCreateDashboardButton?: boolean; }): UseDashboardListingTableReturnType => { - const { - dashboardBackup, - dashboardCapabilities: { showWriteControls }, - settings: { uiSettings }, - dashboardContentManagement: { - findDashboards, - deleteDashboards, - updateDashboardMeta, - checkForDuplicateDashboardTitle, - }, - notifications: { toasts }, - dashboardRecentlyAccessed, - } = pluginServices.getServices(); - const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTableStrings; const title = getTableListTitle(); const entityName = getEntityName(); const entityNamePlural = getEntityNamePlural(); const [pageDataTestSubject, setPageDataTestSubject] = useState(); const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); + + const dashboardBackupService = useMemo(() => getDashboardBackupService(), []); + const dashboardContentManagementService = useMemo( + () => getDashboardContentManagementService(), + [] + ); + const [unsavedDashboardIds, setUnsavedDashboardIds] = useState( - dashboardBackup.getDashboardIdsWithUnsavedChanges() + dashboardBackupService.getDashboardIdsWithUnsavedChanges() ); - const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); - const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); + const listingLimit = coreServices.uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); + const initialPageSize = coreServices.uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); const createItem = useCallback(() => { - if (useSessionStorageIntegration && dashboardBackup.dashboardHasUnsavedEdits()) { + if (useSessionStorageIntegration && dashboardBackupService.dashboardHasUnsavedEdits()) { confirmCreateWithUnsaved(() => { - dashboardBackup.clearState(); + dashboardBackupService.clearState(); goToDashboard(); }, goToDashboard); return; } goToDashboard(); - }, [dashboardBackup, goToDashboard, useSessionStorageIntegration]); + }, [dashboardBackupService, goToDashboard, useSessionStorageIntegration]); const updateItemMeta = useCallback( async (props: Pick) => { - await updateDashboardMeta(props); + await dashboardContentManagementService.updateDashboardMeta(props); - setUnsavedDashboardIds(dashboardBackup.getDashboardIdsWithUnsavedChanges()); + setUnsavedDashboardIds(dashboardBackupService.getDashboardIdsWithUnsavedChanges()); }, - [dashboardBackup, updateDashboardMeta] + [dashboardBackupService, dashboardContentManagementService] ); const contentEditorValidators: OpenContentEditorParams['customValidators'] = useMemo( @@ -145,17 +144,19 @@ export const useDashboardListingTable = ({ fn: async (value: string, id: string) => { if (id) { try { - const [dashboard] = await findDashboards.findByIds([id]); + const [dashboard] = + await dashboardContentManagementService.findDashboards.findByIds([id]); if (dashboard.status === 'error') { return; } - const validTitle = await checkForDuplicateDashboardTitle({ - title: value, - copyOnSave: false, - lastSavedTitle: dashboard.attributes.title, - isTitleDuplicateConfirmed: false, - }); + const validTitle = + await dashboardContentManagementService.checkForDuplicateDashboardTitle({ + title: value, + copyOnSave: false, + lastSavedTitle: dashboard.attributes.title, + isTitleDuplicateConfirmed: false, + }); if (!validTitle) { throw new Error(dashboardListingErrorStrings.getDuplicateTitleWarning(value)); @@ -168,7 +169,7 @@ export const useDashboardListingTable = ({ }, ], }), - [checkForDuplicateDashboardTitle, findDashboards] + [dashboardContentManagementService] ); const emptyPrompt = useMemo( @@ -204,7 +205,7 @@ export const useDashboardListingTable = ({ ) => { const searchStartTime = window.performance.now(); - return findDashboards + return dashboardContentManagementService.findDashboards .search({ search: searchTerm, size: listingLimit, @@ -214,7 +215,7 @@ export const useDashboardListingTable = ({ .then(({ total, hits }) => { const searchEndTime = window.performance.now(); const searchDuration = searchEndTime - searchStartTime; - reportPerformanceMetricEvent(pluginServices.getServices().analytics, { + reportPerformanceMetricEvent(coreServices.analytics, { eventName: SAVED_OBJECT_LOADED_TIME, duration: searchDuration, meta: { @@ -227,7 +228,7 @@ export const useDashboardListingTable = ({ }; }); }, - [findDashboards, listingLimit] + [listingLimit, dashboardContentManagementService] ); const deleteItems = useCallback( @@ -235,15 +236,15 @@ export const useDashboardListingTable = ({ try { const deleteStartTime = window.performance.now(); - await deleteDashboards( + await dashboardContentManagementService.deleteDashboards( dashboardsToDelete.map(({ id }) => { - dashboardBackup.clearState(id); + dashboardBackupService.clearState(id); return id; }) ); const deleteDuration = window.performance.now() - deleteStartTime; - reportPerformanceMetricEvent(pluginServices.getServices().analytics, { + reportPerformanceMetricEvent(coreServices.analytics, { eventName: SAVED_OBJECT_DELETE_TIME, duration: deleteDuration, meta: { @@ -252,14 +253,14 @@ export const useDashboardListingTable = ({ }, }); } catch (error) { - toasts.addError(error, { + coreServices.notifications.toasts.addError(error, { title: dashboardListingErrorStrings.getErrorDeletingDashboardToast(), }); } - setUnsavedDashboardIds(dashboardBackup.getDashboardIdsWithUnsavedChanges()); + setUnsavedDashboardIds(dashboardBackupService.getDashboardIdsWithUnsavedChanges()); }, - [dashboardBackup, deleteDashboards, toasts] + [dashboardBackupService, dashboardContentManagementService] ); const editItem = useCallback( @@ -278,8 +279,9 @@ export const useDashboardListingTable = ({ [getDashboardUrl] ); - const tableListViewTableProps: DashboardListingViewTableProps = useMemo( - () => ({ + const tableListViewTableProps: DashboardListingViewTableProps = useMemo(() => { + const { showWriteControls } = getDashboardCapabilities(); + return { contentEditor: { isReadonly: !showWriteControls, onSave: updateItemMeta, @@ -303,36 +305,38 @@ export const useDashboardListingTable = ({ title, urlStateEnabled, createdByEnabled: true, - recentlyAccessed: dashboardRecentlyAccessed, - }), - [ - contentEditorValidators, - createItem, - dashboardListingId, - deleteItems, - editItem, - emptyPrompt, - entityName, - entityNamePlural, - findItems, - getDetailViewLink, - headingId, - initialFilter, - initialPageSize, - listingLimit, - onFetchSuccess, - showCreateDashboardButton, - showWriteControls, - title, - updateItemMeta, - urlStateEnabled, - dashboardRecentlyAccessed, - ] - ); + recentlyAccessed: getDashboardRecentlyAccessedService(), + }; + }, [ + contentEditorValidators, + createItem, + dashboardListingId, + deleteItems, + editItem, + emptyPrompt, + entityName, + entityNamePlural, + findItems, + getDetailViewLink, + headingId, + initialFilter, + initialPageSize, + listingLimit, + onFetchSuccess, + showCreateDashboardButton, + title, + updateItemMeta, + urlStateEnabled, + ]); const refreshUnsavedDashboards = useCallback( - () => setUnsavedDashboardIds(dashboardBackup.getDashboardIdsWithUnsavedChanges()), - [dashboardBackup] + () => setUnsavedDashboardIds(getDashboardBackupService().getDashboardIdsWithUnsavedChanges()), + [] + ); + + const contentInsightsClient = useMemo( + () => new ContentInsightsClient({ http: coreServices.http }, { domainId: 'dashboard' }), + [] ); return { @@ -341,5 +345,6 @@ export const useDashboardListingTable = ({ refreshUnsavedDashboards, tableListViewTableProps, unsavedDashboardIds, + contentInsightsClient, }; }; diff --git a/src/plugins/dashboard/public/dashboard_listing/types.ts b/src/plugins/dashboard/public/dashboard_listing/types.ts index c5788868b5535..0f5e245366242 100644 --- a/src/plugins/dashboard/public/dashboard_listing/types.ts +++ b/src/plugins/dashboard/public/dashboard_listing/types.ts @@ -10,7 +10,6 @@ import type { PropsWithChildren } from 'react'; import type { UserContentCommonSchema } from '@kbn/content-management-table-list-view-common'; import type { ViewMode } from '@kbn/embeddable-plugin/public'; -import type { DashboardApplicationService } from '../services/application/types'; export type DashboardListingProps = PropsWithChildren<{ disableCreateDashboardButton?: boolean; @@ -22,12 +21,6 @@ export type DashboardListingProps = PropsWithChildren<{ showCreateDashboardButton?: boolean; }>; -// because the type of `application.capabilities.advancedSettings` is so generic, the provider -// requiring the `save` key to be part of it is causing type issues - so, creating a custom type -export type TableListViewApplicationService = DashboardApplicationService & { - capabilities: { advancedSettings: { save: boolean } }; -}; - export interface DashboardSavedObjectUserContent extends UserContentCommonSchema { managed?: boolean; attributes: { diff --git a/src/plugins/dashboard/public/dashboard_top_nav/index.tsx b/src/plugins/dashboard/public/dashboard_top_nav/index.tsx index c8c02abae950d..18c0b69cd9b57 100644 --- a/src/plugins/dashboard/public/dashboard_top_nav/index.tsx +++ b/src/plugins/dashboard/public/dashboard_top_nav/index.tsx @@ -8,13 +8,13 @@ */ import React, { Suspense } from 'react'; -import { servicesReady } from '../plugin'; import { DashboardTopNavProps } from './dashboard_top_nav_with_context'; +import { untilPluginStartServicesReady } from '../services/kibana_services'; const LazyDashboardTopNav = React.lazy(() => (async () => { const modulePromise = import('./dashboard_top_nav_with_context'); - const [module] = await Promise.all([modulePromise, servicesReady]); + const [module] = await Promise.all([modulePromise, untilPluginStartServicesReady()]); return { default: module.DashboardTopNavWithContext, diff --git a/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.test.tsx b/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.test.tsx index 4114b7ab06d06..8cf7f36918ddc 100644 --- a/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.test.tsx +++ b/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.test.tsx @@ -12,10 +12,10 @@ import { render } from '@testing-library/react'; import { buildMockDashboard } from '../mocks'; import { InternalDashboardTopNav } from './internal_dashboard_top_nav'; import { setMockedPresentationUtilServices } from '@kbn/presentation-util-plugin/public/mocks'; -import { pluginServices } from '../services/plugin_services'; import { TopNavMenuProps } from '@kbn/navigation-plugin/public'; import { DashboardContext } from '../dashboard_api/use_dashboard_api'; import { DashboardApi } from '../dashboard_api/types'; +import { dataService, navigationService } from '../services/kibana_services'; describe('Internal dashboard top nav', () => { const mockTopNav = (badges: TopNavMenuProps['badges'] | undefined[]) => { @@ -32,13 +32,10 @@ describe('Internal dashboard top nav', () => { beforeEach(() => { setMockedPresentationUtilServices(); - pluginServices.getServices().data.query.filterManager.getFilters = jest - .fn() - .mockReturnValue([]); + dataService.query.filterManager.getFilters = jest.fn().mockReturnValue([]); // topNavMenu is mocked as a jest.fn() so we want to mock it with a component // @ts-ignore type issue with the mockTopNav for this test suite - pluginServices.getServices().navigation.TopNavMenu = ({ badges }: TopNavMenuProps) => - mockTopNav(badges); + navigationService.ui.TopNavMenu = ({ badges }: TopNavMenuProps) => mockTopNav(badges); }); it('should not render the managed badge by default', async () => { diff --git a/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx b/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx index 02cbaa7d90100..bcfcf2e98dddf 100644 --- a/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx @@ -7,48 +7,57 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import UseUnmount from 'react-use/lib/useUnmount'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import UseUnmount from 'react-use/lib/useUnmount'; import { - withSuspense, - LazyLabsFlyout, - getContextProvider as getPresentationUtilContextProvider, -} from '@kbn/presentation-util-plugin/public'; -import { TopNavMenuBadgeProps, TopNavMenuProps } from '@kbn/navigation-plugin/public'; -import { + EuiBadge, EuiBreadcrumb, EuiHorizontalRule, EuiIcon, - EuiToolTipProps, - EuiPopover, - EuiBadge, EuiLink, + EuiPopover, + EuiToolTipProps, } from '@elastic/eui'; import { MountPoint } from '@kbn/core/public'; -import { getManagedContentBadge } from '@kbn/managed-content-badge'; +import { Query } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; +import { getManagedContentBadge } from '@kbn/managed-content-badge'; +import { TopNavMenuBadgeProps, TopNavMenuProps } from '@kbn/navigation-plugin/public'; import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; -import { Query } from '@kbn/es-query'; import { + LazyLabsFlyout, + getContextProvider as getPresentationUtilContextProvider, + withSuspense, +} from '@kbn/presentation-util-plugin/public'; + +import { UI_SETTINGS } from '../../common'; +import { useDashboardApi } from '../dashboard_api/use_dashboard_api'; +import { + dashboardManagedBadge, + getDashboardBreadcrumb, getDashboardTitle, leaveConfirmStrings, - getDashboardBreadcrumb, unsavedChangesBadgeStrings, - dashboardManagedBadge, } from '../dashboard_app/_dashboard_app_strings'; -import { UI_SETTINGS } from '../../common'; -import { pluginServices } from '../services/plugin_services'; +import { useDashboardMountContext } from '../dashboard_app/hooks/dashboard_mount_context'; +import { DashboardEditingToolbar } from '../dashboard_app/top_nav/dashboard_editing_toolbar'; import { useDashboardMenuItems } from '../dashboard_app/top_nav/use_dashboard_menu_items'; import { DashboardEmbedSettings } from '../dashboard_app/types'; -import { DashboardEditingToolbar } from '../dashboard_app/top_nav/dashboard_editing_toolbar'; -import { useDashboardMountContext } from '../dashboard_app/hooks/dashboard_mount_context'; -import { getFullEditPath, LEGACY_DASHBOARD_APP_ID } from '../dashboard_constants'; -import './_dashboard_top_nav.scss'; -import { DashboardRedirect } from '../dashboard_container/types'; -import { SaveDashboardReturn } from '../services/dashboard_content_management/types'; -import { useDashboardApi } from '../dashboard_api/use_dashboard_api'; +import { LEGACY_DASHBOARD_APP_ID, getFullEditPath } from '../dashboard_constants'; import { openSettingsFlyout } from '../dashboard_container/embeddable/api'; +import { DashboardRedirect } from '../dashboard_container/types'; +import { SaveDashboardReturn } from '../services/dashboard_content_management_service/types'; +import { getDashboardRecentlyAccessedService } from '../services/dashboard_recently_accessed_service'; +import { + coreServices, + dataService, + embeddableService, + navigationService, + serverlessService, +} from '../services/kibana_services'; +import { getDashboardCapabilities } from '../utils/get_dashboard_capabilities'; +import './_dashboard_top_nav.scss'; export interface InternalDashboardTopNavProps { customLeadingBreadCrumbs?: EuiBreadcrumb[]; @@ -75,28 +84,7 @@ export function InternalDashboardTopNav({ const [isLabsShown, setIsLabsShown] = useState(false); const dashboardTitleRef = useRef(null); - /** - * Unpack dashboard services - */ - const { - data: { - query: { filterManager }, - }, - chrome: { - setBreadcrumbs, - setIsVisible: setChromeVisibility, - getIsVisible$: getChromeIsVisible$, - recentlyAccessed: chromeRecentlyAccessed, - }, - serverless, - settings: { uiSettings }, - navigation: { TopNavMenu }, - embeddable: { getStateTransfer }, - initializerContext: { allowByValueEmbeddables }, - dashboardCapabilities: { saveQuery: allowSaveQuery, showWriteControls }, - dashboardRecentlyAccessed, - } = pluginServices.getServices(); - const isLabsEnabled = uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI); + const isLabsEnabled = useMemo(() => coreServices.uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI), []); const { setHeaderActionMenu, onAppLeave } = useDashboardMountContext(); const dashboardApi = useDashboardApi(); @@ -144,36 +132,24 @@ export function InternalDashboardTopNav({ * Manage chrome visibility when dashboard is embedded. */ useEffect(() => { - if (!embedSettings) setChromeVisibility(viewMode !== 'print'); - }, [embedSettings, setChromeVisibility, viewMode]); + if (!embedSettings) coreServices.chrome.setIsVisible(viewMode !== 'print'); + }, [embedSettings, viewMode]); /** * populate recently accessed, and set is chrome visible. */ useEffect(() => { - const subscription = getChromeIsVisible$().subscribe((visible) => setIsChromeVisible(visible)); + const subscription = coreServices.chrome + .getIsVisible$() + .subscribe((visible) => setIsChromeVisible(visible)); + if (lastSavedId && title) { - chromeRecentlyAccessed.add( - getFullEditPath(lastSavedId, viewMode === 'edit'), - title, - lastSavedId - ); - dashboardRecentlyAccessed.add( - getFullEditPath(lastSavedId, viewMode === 'edit'), - title, - lastSavedId - ); + const fullEditPath = getFullEditPath(lastSavedId, viewMode === 'edit'); + coreServices.chrome.recentlyAccessed.add(fullEditPath, title, lastSavedId); + getDashboardRecentlyAccessedService().add(fullEditPath, title, lastSavedId); // used to sort the listing table } return () => subscription.unsubscribe(); - }, [ - allowByValueEmbeddables, - chromeRecentlyAccessed, - getChromeIsVisible$, - lastSavedId, - viewMode, - title, - dashboardRecentlyAccessed, - ]); + }, [lastSavedId, viewMode, title]); /** * Set breadcrumbs to dashboard title when dashboard's title or view mode changes @@ -198,17 +174,17 @@ export function InternalDashboardTopNav({ }, ]; - if (serverless?.setBreadcrumbs) { + if (serverlessService) { // set serverless breadcrumbs if available, // set only the dashboardTitleBreadcrumbs because the main breadcrumbs automatically come as part of the navigation config - serverless.setBreadcrumbs(dashboardTitleBreadcrumbs); + serverlessService.setBreadcrumbs(dashboardTitleBreadcrumbs); } else { /** * non-serverless regular breadcrumbs * Dashboard embedded in other plugins (e.g. SecuritySolution) * will have custom leading breadcrumbs for back to their app. **/ - setBreadcrumbs( + coreServices.chrome.setBreadcrumbs( customLeadingBreadCrumbs.concat([ { text: getDashboardBreadcrumb(), @@ -221,22 +197,18 @@ export function InternalDashboardTopNav({ ]) ); } - }, [ - setBreadcrumbs, - redirectTo, - dashboardTitle, - dashboardApi, - viewMode, - serverless, - customLeadingBreadCrumbs, - ]); + }, [redirectTo, dashboardTitle, dashboardApi, viewMode, customLeadingBreadCrumbs]); /** * Build app leave handler whenever hasUnsavedChanges changes */ useEffect(() => { onAppLeave((actions) => { - if (viewMode === 'edit' && hasUnsavedChanges && !getStateTransfer().isTransferInProgress) { + if ( + viewMode === 'edit' && + hasUnsavedChanges && + !embeddableService.getStateTransfer().isTransferInProgress + ) { return actions.confirm( leaveConfirmStrings.getLeaveSubtitle(), leaveConfirmStrings.getLeaveTitle() @@ -248,13 +220,13 @@ export function InternalDashboardTopNav({ // reset on app leave handler so leaving from the listing page doesn't trigger a confirmation onAppLeave((actions) => actions.default()); }; - }, [onAppLeave, getStateTransfer, hasUnsavedChanges, viewMode]); + }, [onAppLeave, hasUnsavedChanges, viewMode]); const visibilityProps = useMemo(() => { const shouldShowNavBarComponent = (forceShow: boolean): boolean => (forceShow || isChromeVisible) && !fullScreenMode; const shouldShowFilterBar = (forceHide: boolean): boolean => - !forceHide && (filterManager.getFilters().length > 0 || !fullScreenMode); + !forceHide && (dataService.query.filterManager.getFilters().length > 0 || !fullScreenMode); const showTopNavMenu = shouldShowNavBarComponent(Boolean(embedSettings?.forceShowTopNavMenu)); const showQueryInput = Boolean(forceHideUnifiedSearch) @@ -275,14 +247,7 @@ export function InternalDashboardTopNav({ showQueryInput, showDatePicker, }; - }, [ - embedSettings, - filterManager, - forceHideUnifiedSearch, - fullScreenMode, - isChromeVisible, - viewMode, - ]); + }, [embedSettings, forceHideUnifiedSearch, fullScreenMode, isChromeVisible, viewMode]); const maybeRedirect = useCallback( (result?: SaveDashboardReturn) => { @@ -338,6 +303,8 @@ export function InternalDashboardTopNav({ } as EuiToolTipProps, }); } + + const { showWriteControls } = getDashboardCapabilities(); if (showWriteControls && managed) { const badgeProps = { ...getManagedContentBadge(dashboardManagedBadge.getBadgeAriaLabel()), @@ -390,7 +357,6 @@ export function InternalDashboardTopNav({ hasUnsavedChanges, viewMode, hasRunMigrations, - showWriteControls, managed, isPopoverOpen, dashboardApi, @@ -405,7 +371,7 @@ export function InternalDashboardTopNav({ ref={dashboardTitleRef} tabIndex={-1} >{`${getDashboardBreadcrumb()} - ${dashboardTitle}`} - { - return pluginServices.getServices(); -}; export type Start = jest.Mocked; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 33f9115fa6763..c46dcbc3e4139 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -7,75 +7,78 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { i18n } from '@kbn/i18n'; -import { BehaviorSubject } from 'rxjs'; -import { filter, map } from 'rxjs'; +import { BehaviorSubject, filter, map } from 'rxjs'; +import type { + ContentManagementPublicSetup, + ContentManagementPublicStart, +} from '@kbn/content-management-plugin/public'; +import { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import { + APP_WRAPPER_CLASS, App, - Plugin, + AppMountParameters, AppUpdater, + DEFAULT_APP_CATEGORIES, + Plugin, + PluginInitializerContext, ScopedHistory, type CoreSetup, type CoreStart, - AppMountParameters, - DEFAULT_APP_CATEGORIES, - PluginInitializerContext, } from '@kbn/core/public'; -import type { - ScreenshotModePluginSetup, - ScreenshotModePluginStart, -} from '@kbn/screenshot-mode-plugin/public'; -import type { - UsageCollectionSetup, - UsageCollectionStart, -} from '@kbn/usage-collection-plugin/public'; -import { APP_WRAPPER_CLASS } from '@kbn/core/public'; -import { type UiActionsSetup, type UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; +import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; +import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public/plugin'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; +import { i18n } from '@kbn/i18n'; +import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/common'; import { createKbnUrlTracker } from '@kbn/kibana-utils-plugin/public'; -import type { VisualizationsStart } from '@kbn/visualizations-plugin/public'; -import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; -import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; -import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; -import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; -import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; -import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import type { - ContentManagementPublicSetup, - ContentManagementPublicStart, -} from '@kbn/content-management-plugin/public'; -import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; -import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import type { ServerlessPluginStart } from '@kbn/serverless/public'; import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import type { ObservabilityAIAssistantPublicSetup, ObservabilityAIAssistantPublicStart, } from '@kbn/observability-ai-assistant-plugin/public'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import type { + ScreenshotModePluginSetup, + ScreenshotModePluginStart, +} from '@kbn/screenshot-mode-plugin/public'; +import type { ServerlessPluginStart } from '@kbn/serverless/public'; +import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; +import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; +import { type UiActionsSetup, type UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; +import type { + UsageCollectionSetup, + UsageCollectionStart, +} from '@kbn/usage-collection-plugin/public'; +import type { VisualizationsStart } from '@kbn/visualizations-plugin/public'; -import { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; -import { DashboardContainerFactoryDefinition } from './dashboard_container/embeddable/dashboard_container_factory'; -import { registerDashboardPanelPlacementSetting } from './dashboard_container/panel_placement'; +import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; import { - type DashboardAppLocator, DashboardAppLocatorDefinition, + type DashboardAppLocator, } from './dashboard_app/locator/locator'; +import { DashboardMountContextProps } from './dashboard_app/types'; import { DASHBOARD_APP_ID, LANDING_PAGE_PATH, LEGACY_DASHBOARD_APP_ID, SEARCH_SESSION_ID, } from './dashboard_constants'; -import { DashboardMountContextProps } from './dashboard_app/types'; -import type { FindDashboardsService } from './services/dashboard_content_management/types'; -import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; -import { GetPanelPlacementSettings } from './dashboard_container/panel_placement'; +import { DashboardContainerFactoryDefinition } from './dashboard_container/embeddable/dashboard_container_factory'; +import { + GetPanelPlacementSettings, + registerDashboardPanelPlacementSetting, +} from './dashboard_container/panel_placement'; +import type { FindDashboardsService } from './services/dashboard_content_management_service/types'; +import { setKibanaServices, untilPluginStartServicesReady } from './services/kibana_services'; export interface DashboardFeatureFlagConfig { allowByValueEmbeddables: boolean; @@ -99,6 +102,7 @@ export interface DashboardStartDependencies { data: DataPublicPluginStart; dataViewEditor: DataViewEditorStart; embeddable: EmbeddableStart; + fieldFormats: FieldFormatsStart; inspector: InspectorStartContract; navigation: NavigationPublicPluginStart; presentationUtil: PresentationUtilPluginStart; @@ -148,27 +152,9 @@ export class DashboardPlugin private dashboardFeatureFlagConfig?: DashboardFeatureFlagConfig; private locator?: DashboardAppLocator; - private async startDashboardKibanaServices( - coreStart: CoreStart, - startPlugins: DashboardStartDependencies, - initContext: PluginInitializerContext - ) { - const { registry, pluginServices } = await import('./services/plugin_services'); - pluginServices.setRegistry(registry.start({ coreStart, startPlugins, initContext })); - resolveServicesReady(); - } - public setup( core: CoreSetup, - { - share, - embeddable, - home, - urlForwarding, - data, - contentManagement, - uiActions, - }: DashboardSetupDependencies + { share, embeddable, home, urlForwarding, data, contentManagement }: DashboardSetupDependencies ): DashboardSetup { this.dashboardFeatureFlagConfig = this.initializerContext.config.get(); @@ -183,11 +169,14 @@ export class DashboardPlugin new DashboardAppLocatorDefinition({ useHashedUrl: core.uiSettings.get('state:storeInSessionStorage'), getDashboardFilterFields: async (dashboardId: string) => { - const { pluginServices } = await import('./services/plugin_services'); - const { - dashboardContentManagement: { loadDashboardState }, - } = pluginServices.getServices(); - return (await loadDashboardState({ id: dashboardId })).dashboardInput?.filters ?? []; + const [{ getDashboardContentManagementService }] = await Promise.all([ + import('./services/dashboard_content_management_service'), + untilPluginStartServicesReady(), + ]); + return ( + (await getDashboardContentManagementService().loadDashboardState({ id: dashboardId })) + .dashboardInput?.filters ?? [] + ); }, }) ); @@ -262,6 +251,7 @@ export class DashboardPlugin mount: async (params: AppMountParameters) => { this.currentHistory = params.history; params.element.classList.add(APP_WRAPPER_CLASS); + await untilPluginStartServicesReady(); const { mountApp } = await import('./dashboard_app/dashboard_router'); appMounted(); @@ -340,25 +330,26 @@ export class DashboardPlugin } public start(core: CoreStart, plugins: DashboardStartDependencies): DashboardStart { - this.startDashboardKibanaServices(core, plugins, this.initializerContext).then(async () => { - const { buildAllDashboardActions } = await import('./dashboard_actions'); - buildAllDashboardActions({ - core, - plugins, - allowByValueEmbeddables: this.dashboardFeatureFlagConfig?.allowByValueEmbeddables, - }); - }); + setKibanaServices(core, plugins); + + Promise.all([import('./dashboard_actions'), untilPluginStartServicesReady()]).then( + ([{ buildAllDashboardActions }]) => { + buildAllDashboardActions({ + plugins, + allowByValueEmbeddables: this.dashboardFeatureFlagConfig?.allowByValueEmbeddables, + }); + } + ); return { locator: this.locator, dashboardFeatureFlagConfig: this.dashboardFeatureFlagConfig!, registerDashboardPanelPlacementSetting, findDashboardsService: async () => { - const { pluginServices } = await import('./services/plugin_services'); - const { - dashboardContentManagement: { findDashboards }, - } = pluginServices.getServices(); - return findDashboards; + const { getDashboardContentManagementService } = await import( + './services/dashboard_content_management_service' + ); + return getDashboardContentManagementService().findDashboards; }, }; } diff --git a/src/plugins/dashboard/public/services/analytics/analytics.stub.ts b/src/plugins/dashboard/public/services/analytics/analytics.stub.ts deleted file mode 100644 index a5baa963a276c..0000000000000 --- a/src/plugins/dashboard/public/services/analytics/analytics.stub.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardAnalyticsService } from './types'; - -type AnalyticsServiceFactory = PluginServiceFactory; - -export const analyticsServiceFactory: AnalyticsServiceFactory = () => { - const pluginMock = analyticsServiceMock.createAnalyticsServiceStart(); - - return { - reportEvent: pluginMock.reportEvent, - }; -}; diff --git a/src/plugins/dashboard/public/services/analytics/analytics_service.ts b/src/plugins/dashboard/public/services/analytics/analytics_service.ts deleted file mode 100644 index f027963d30cda..0000000000000 --- a/src/plugins/dashboard/public/services/analytics/analytics_service.ts +++ /dev/null @@ -1,27 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardAnalyticsService } from './types'; - -export type AnalyticsServiceFactory = KibanaPluginServiceFactory< - DashboardAnalyticsService, - DashboardStartDependencies ->; - -export const analyticsServiceFactory: AnalyticsServiceFactory = ({ coreStart }) => { - const { - analytics: { reportEvent }, - } = coreStart; - - return { - reportEvent, - }; -}; diff --git a/src/plugins/dashboard/public/services/analytics/types.ts b/src/plugins/dashboard/public/services/analytics/types.ts deleted file mode 100644 index fb0423229709f..0000000000000 --- a/src/plugins/dashboard/public/services/analytics/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardAnalyticsService { - reportEvent: CoreStart['analytics']['reportEvent']; -} diff --git a/src/plugins/dashboard/public/services/application/application.stub.ts b/src/plugins/dashboard/public/services/application/application.stub.ts deleted file mode 100644 index 83565a9302a96..0000000000000 --- a/src/plugins/dashboard/public/services/application/application.stub.ts +++ /dev/null @@ -1,31 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardApplicationService } from './types'; - -type ApplicationServiceFactory = PluginServiceFactory; - -export const applicationServiceFactory: ApplicationServiceFactory = () => { - const pluginMock = applicationServiceMock.createStartContract(); - - return { - currentAppId$: pluginMock.currentAppId$, - navigateToApp: pluginMock.navigateToApp, - navigateToUrl: pluginMock.navigateToUrl, - getUrlForApp: pluginMock.getUrlForApp, - capabilities: { - advancedSettings: pluginMock.capabilities.advancedSettings, - maps: pluginMock.capabilities.maps, - navLinks: pluginMock.capabilities.navLinks, - visualize: pluginMock.capabilities.visualize, - }, - }; -}; diff --git a/src/plugins/dashboard/public/services/application/application_service.ts b/src/plugins/dashboard/public/services/application/application_service.ts deleted file mode 100644 index bf4af0719c2ad..0000000000000 --- a/src/plugins/dashboard/public/services/application/application_service.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardApplicationService } from './types'; - -export type ApplicationServiceFactory = KibanaPluginServiceFactory< - DashboardApplicationService, - DashboardStartDependencies ->; - -export const applicationServiceFactory: ApplicationServiceFactory = ({ coreStart }) => { - const { - application: { - currentAppId$, - navigateToApp, - navigateToUrl, - getUrlForApp, - capabilities: { advancedSettings, maps, navLinks, visualize }, - }, - } = coreStart; - - return { - currentAppId$, - navigateToApp, - navigateToUrl, - getUrlForApp, - capabilities: { - advancedSettings, - maps, - navLinks, - visualize, - }, - }; -}; diff --git a/src/plugins/dashboard/public/services/application/types.ts b/src/plugins/dashboard/public/services/application/types.ts deleted file mode 100644 index 16041562257bf..0000000000000 --- a/src/plugins/dashboard/public/services/application/types.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardApplicationService { - currentAppId$: CoreStart['application']['currentAppId$']; - navigateToApp: CoreStart['application']['navigateToApp']; - navigateToUrl: CoreStart['application']['navigateToUrl']; - getUrlForApp: CoreStart['application']['getUrlForApp']; - capabilities: { - advancedSettings: CoreStart['application']['capabilities']['advancedSettings']; - maps: CoreStart['application']['capabilities']['maps']; // only used in `add_to_library_action` - navLinks: CoreStart['application']['capabilities']['navLinks']; - visualize: CoreStart['application']['capabilities']['visualize']; // only used in `add_to_library_action` - }; -} diff --git a/src/plugins/dashboard/public/services/chrome/chrome.stub.ts b/src/plugins/dashboard/public/services/chrome/chrome.stub.ts deleted file mode 100644 index 70a2635f3a8b5..0000000000000 --- a/src/plugins/dashboard/public/services/chrome/chrome.stub.ts +++ /dev/null @@ -1,29 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { coreMock, chromeServiceMock } from '@kbn/core/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardChromeService } from './types'; - -type ChromeServiceFactory = PluginServiceFactory; - -export const chromeServiceFactory: ChromeServiceFactory = () => { - const pluginMock = chromeServiceMock.createStartContract(); - - return { - docTitle: pluginMock.docTitle, - setBadge: pluginMock.setBadge, - getIsVisible$: pluginMock.getIsVisible$, - recentlyAccessed: pluginMock.recentlyAccessed, - setBreadcrumbs: pluginMock.setBreadcrumbs, - setHelpExtension: pluginMock.setHelpExtension, - setIsVisible: pluginMock.setIsVisible, - theme: coreMock.createStart().theme, - }; -}; diff --git a/src/plugins/dashboard/public/services/chrome/chrome_service.ts b/src/plugins/dashboard/public/services/chrome/chrome_service.ts deleted file mode 100644 index 43458bd71cd06..0000000000000 --- a/src/plugins/dashboard/public/services/chrome/chrome_service.ts +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardChromeService } from './types'; - -export type ChromeServiceFactory = KibanaPluginServiceFactory< - DashboardChromeService, - DashboardStartDependencies ->; - -export const chromeServiceFactory: ChromeServiceFactory = ({ coreStart }) => { - const { - chrome: { - docTitle, - setBadge, - getIsVisible$, - recentlyAccessed, - setBreadcrumbs, - setHelpExtension, - setIsVisible, - }, - theme, - } = coreStart; - - return { - docTitle, - setBadge, - getIsVisible$, - recentlyAccessed, - setBreadcrumbs, - setHelpExtension, - setIsVisible, - theme, - }; -}; diff --git a/src/plugins/dashboard/public/services/chrome/types.ts b/src/plugins/dashboard/public/services/chrome/types.ts deleted file mode 100644 index 983073aab75f9..0000000000000 --- a/src/plugins/dashboard/public/services/chrome/types.ts +++ /dev/null @@ -1,21 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardChromeService { - docTitle: CoreStart['chrome']['docTitle']; - setBadge: CoreStart['chrome']['setBadge']; - getIsVisible$: CoreStart['chrome']['getIsVisible$']; - recentlyAccessed: CoreStart['chrome']['recentlyAccessed']; - setBreadcrumbs: CoreStart['chrome']['setBreadcrumbs']; - setHelpExtension: CoreStart['chrome']['setHelpExtension']; - setIsVisible: CoreStart['chrome']['setIsVisible']; - theme: CoreStart['theme']; -} diff --git a/src/plugins/dashboard/public/services/content_management/content_management_service.stub.ts b/src/plugins/dashboard/public/services/content_management/content_management_service.stub.ts deleted file mode 100644 index 21bf785fb611c..0000000000000 --- a/src/plugins/dashboard/public/services/content_management/content_management_service.stub.ts +++ /dev/null @@ -1,18 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; -import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; - -export type ContentManagementServiceFactory = PluginServiceFactory; - -export const contentManagementServiceFactory: ContentManagementServiceFactory = () => { - return contentManagementMock.createStartContract(); -}; diff --git a/src/plugins/dashboard/public/services/content_management/content_management_service.ts b/src/plugins/dashboard/public/services/content_management/content_management_service.ts deleted file mode 100644 index c5c94ef91a31c..0000000000000 --- a/src/plugins/dashboard/public/services/content_management/content_management_service.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; -import { DashboardStartDependencies } from '../../plugin'; - -export type ContentManagementServiceFactory = KibanaPluginServiceFactory< - ContentManagementPublicStart, - DashboardStartDependencies ->; - -export const contentManagementServiceFactory: ContentManagementServiceFactory = ({ - startPlugins, -}) => { - const { contentManagement } = startPlugins; - - return contentManagement; -}; diff --git a/src/plugins/dashboard/public/services/core_context/core_context.stub.ts b/src/plugins/dashboard/public/services/core_context/core_context.stub.ts deleted file mode 100644 index 36d1f19b7cd30..0000000000000 --- a/src/plugins/dashboard/public/services/core_context/core_context.stub.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { coreMock } from '@kbn/core/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardCoreContextService } from './types'; - -type CoreContextServiceFactory = PluginServiceFactory; - -export const coreContextServiceFactory: CoreContextServiceFactory = () => { - const pluginMock = coreMock.createStart(); - return { - executionContext: pluginMock.executionContext, - i18nContext: pluginMock.i18n.Context, - }; -}; diff --git a/src/plugins/dashboard/public/services/core_context/core_context_service.ts b/src/plugins/dashboard/public/services/core_context/core_context_service.ts deleted file mode 100644 index 38b8834455ce9..0000000000000 --- a/src/plugins/dashboard/public/services/core_context/core_context_service.ts +++ /dev/null @@ -1,29 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardCoreContextService } from './types'; - -export type CoreContextServiceFactory = KibanaPluginServiceFactory< - DashboardCoreContextService, - DashboardStartDependencies ->; - -export const coreContextServiceFactory: CoreContextServiceFactory = ({ coreStart }) => { - const { - executionContext, - i18n: { Context }, - } = coreStart; - - return { - executionContext, - i18nContext: Context, - }; -}; diff --git a/src/plugins/dashboard/public/services/core_context/types.ts b/src/plugins/dashboard/public/services/core_context/types.ts deleted file mode 100644 index 23f3fc7b5a7c3..0000000000000 --- a/src/plugins/dashboard/public/services/core_context/types.ts +++ /dev/null @@ -1,15 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardCoreContextService { - executionContext: CoreStart['executionContext']; - i18nContext: CoreStart['i18n']['Context']; -} diff --git a/src/plugins/dashboard/public/services/custom_branding/custom_branding.stub.ts b/src/plugins/dashboard/public/services/custom_branding/custom_branding.stub.ts deleted file mode 100644 index 6bcb0ba4172b2..0000000000000 --- a/src/plugins/dashboard/public/services/custom_branding/custom_branding.stub.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { coreMock } from '@kbn/core/public/mocks'; -import { DashboardCustomBrandingService } from './types'; - -type CustomBrandingServiceFactory = PluginServiceFactory; - -export const customBrandingServiceFactory: CustomBrandingServiceFactory = () => { - const pluginMock = coreMock.createStart(); - return { - hasCustomBranding$: pluginMock.customBranding.hasCustomBranding$, - customBranding$: pluginMock.customBranding.customBranding$, - }; -}; diff --git a/src/plugins/dashboard/public/services/custom_branding/custom_branding_service.ts b/src/plugins/dashboard/public/services/custom_branding/custom_branding_service.ts deleted file mode 100644 index bed7d1ac229fc..0000000000000 --- a/src/plugins/dashboard/public/services/custom_branding/custom_branding_service.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardStartDependencies } from '../../plugin'; -import { DashboardCustomBrandingService } from './types'; - -export type CustomBrandingServiceFactory = KibanaPluginServiceFactory< - DashboardCustomBrandingService, - DashboardStartDependencies ->; - -export const customBrandingServiceFactory: CustomBrandingServiceFactory = ({ coreStart }) => { - const { customBranding } = coreStart; - return { - hasCustomBranding$: customBranding.hasCustomBranding$, - customBranding$: customBranding.customBranding$, - }; -}; diff --git a/src/plugins/dashboard/public/services/custom_branding/types.ts b/src/plugins/dashboard/public/services/custom_branding/types.ts deleted file mode 100644 index f2cd986b2db1c..0000000000000 --- a/src/plugins/dashboard/public/services/custom_branding/types.ts +++ /dev/null @@ -1,15 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; - -export interface DashboardCustomBrandingService { - hasCustomBranding$: CustomBrandingStart['hasCustomBranding$']; - customBranding$: CustomBrandingStart['customBranding$']; -} diff --git a/src/plugins/dashboard/public/services/dashboard_backup/dashboard_backup.stub.ts b/src/plugins/dashboard/public/services/dashboard_backup/dashboard_backup.stub.ts deleted file mode 100644 index 4c80fcff9e828..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_backup/dashboard_backup.stub.ts +++ /dev/null @@ -1,27 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardBackupServiceType } from './types'; - -type DashboardBackupServiceFactory = PluginServiceFactory; - -export const dashboardBackupServiceFactory: DashboardBackupServiceFactory = () => { - return { - clearState: jest.fn(), - getState: jest.fn().mockReturnValue(undefined), - setState: jest.fn(), - getViewMode: jest.fn(), - storeViewMode: jest.fn(), - getDashboardIdsWithUnsavedChanges: jest - .fn() - .mockReturnValue(['dashboardUnsavedOne', 'dashboardUnsavedTwo']), - dashboardHasUnsavedEdits: jest.fn(), - }; -}; diff --git a/src/plugins/dashboard/public/services/dashboard_backup/types.ts b/src/plugins/dashboard/public/services/dashboard_backup/types.ts deleted file mode 100644 index d7b7416d5810d..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_backup/types.ts +++ /dev/null @@ -1,31 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { UnsavedPanelState } from '../../dashboard_container/types'; -import { SavedDashboardInput } from '../dashboard_content_management/types'; - -export interface DashboardBackupServiceType { - clearState: (id?: string) => void; - getState: (id: string | undefined) => - | { - dashboardState?: Partial; - panels?: UnsavedPanelState; - } - | undefined; - setState: ( - id: string | undefined, - dashboardState: Partial, - panels: UnsavedPanelState - ) => void; - getViewMode: () => ViewMode; - storeViewMode: (viewMode: ViewMode) => void; - getDashboardIdsWithUnsavedChanges: () => string[]; - dashboardHasUnsavedEdits: (id?: string) => boolean; -} diff --git a/src/plugins/dashboard/public/services/dashboard_backup/dashboard_backup_service.ts b/src/plugins/dashboard/public/services/dashboard_backup_service.ts similarity index 78% rename from src/plugins/dashboard/public/services/dashboard_backup/dashboard_backup_service.ts rename to src/plugins/dashboard/public/services/dashboard_backup_service.ts index ea56cd8ed5d5e..5ffff35ff3d77 100644 --- a/src/plugins/dashboard/public/services/dashboard_backup/dashboard_backup_service.ts +++ b/src/plugins/dashboard/public/services/dashboard_backup_service.ts @@ -7,21 +7,18 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { firstValueFrom } from 'rxjs'; import { isEqual } from 'lodash'; +import { firstValueFrom } from 'rxjs'; -import { set } from '@kbn/safer-lodash-set'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { set } from '@kbn/safer-lodash-set'; -import { DashboardSpacesService } from '../spaces/types'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardBackupServiceType } from './types'; -import type { DashboardContainerInput } from '../../../common'; -import { DashboardNotificationsService } from '../notifications/types'; -import { backupServiceStrings } from '../../dashboard_container/_dashboard_container_strings'; -import { UnsavedPanelState } from '../../dashboard_container/types'; +import type { DashboardContainerInput } from '../../common'; +import { backupServiceStrings } from '../dashboard_container/_dashboard_container_strings'; +import { UnsavedPanelState } from '../dashboard_container/types'; +import { coreServices, spacesService } from './kibana_services'; +import { SavedDashboardInput } from './dashboard_content_management_service/types'; export const DASHBOARD_PANELS_UNSAVED_ID = 'unsavedDashboard'; export const PANELS_CONTROL_GROUP_KEY = 'controlGroup'; @@ -31,34 +28,39 @@ const DASHBOARD_VIEWMODE_LOCAL_KEY = 'dashboardViewMode'; // this key is named `panels` for BWC reasons, but actually contains the entire dashboard state const DASHBOARD_STATE_SESSION_KEY = 'dashboardStateManagerPanels'; -interface DashboardBackupRequiredServices { - notifications: DashboardNotificationsService; - spaces: DashboardSpacesService; +interface DashboardBackupServiceType { + clearState: (id?: string) => void; + getState: (id: string | undefined) => + | { + dashboardState?: Partial; + panels?: UnsavedPanelState; + } + | undefined; + setState: ( + id: string | undefined, + dashboardState: Partial, + panels: UnsavedPanelState + ) => void; + getViewMode: () => ViewMode; + storeViewMode: (viewMode: ViewMode) => void; + getDashboardIdsWithUnsavedChanges: () => string[]; + dashboardHasUnsavedEdits: (id?: string) => boolean; } -export type DashboardBackupServiceFactory = KibanaPluginServiceFactory< - DashboardBackupServiceType, - DashboardStartDependencies, - DashboardBackupRequiredServices ->; - class DashboardBackupService implements DashboardBackupServiceType { private activeSpaceId: string; private sessionStorage: Storage; private localStorage: Storage; - private notifications: DashboardNotificationsService; - private spaces: DashboardSpacesService; private oldDashboardsWithUnsavedChanges: string[] = []; - constructor(requiredServices: DashboardBackupRequiredServices) { - ({ notifications: this.notifications, spaces: this.spaces } = requiredServices); + constructor() { this.sessionStorage = new Storage(sessionStorage); this.localStorage = new Storage(localStorage); this.activeSpaceId = 'default'; - if (this.spaces.getActiveSpace$) { - firstValueFrom(this.spaces.getActiveSpace$()).then((space) => { + if (spacesService) { + firstValueFrom(spacesService.getActiveSpace$()).then((space) => { this.activeSpaceId = space.id; }); } @@ -72,7 +74,7 @@ class DashboardBackupService implements DashboardBackupServiceType { try { this.localStorage.set(DASHBOARD_VIEWMODE_LOCAL_KEY, viewMode); } catch (e) { - this.notifications.toasts.addDanger({ + coreServices.notifications.toasts.addDanger({ title: backupServiceStrings.viewModeStorageError(e.message), 'data-test-subj': 'dashboardViewmodeBackupFailure', }); @@ -99,7 +101,7 @@ class DashboardBackupService implements DashboardBackupServiceType { }); } } catch (e) { - this.notifications.toasts.addDanger({ + coreServices.notifications.toasts.addDanger({ title: backupServiceStrings.getPanelsClearError(e.message), 'data-test-subj': 'dashboardPanelsClearFailure', }); @@ -117,7 +119,7 @@ class DashboardBackupService implements DashboardBackupServiceType { return { dashboardState, panels }; } catch (e) { - this.notifications.toasts.addDanger({ + coreServices.notifications.toasts.addDanger({ title: backupServiceStrings.getPanelsGetError(e.message), 'data-test-subj': 'dashboardPanelsGetFailure', }); @@ -138,7 +140,7 @@ class DashboardBackupService implements DashboardBackupServiceType { set(panelsStorage, [this.activeSpaceId, id], unsavedPanels); this.sessionStorage.set(DASHBOARD_PANELS_SESSION_KEY, panelsStorage, true); } catch (e) { - this.notifications.toasts.addDanger({ + coreServices.notifications.toasts.addDanger({ title: backupServiceStrings.getPanelsSetError(e.message), 'data-test-subj': 'dashboardPanelsSetFailure', }); @@ -178,7 +180,7 @@ class DashboardBackupService implements DashboardBackupServiceType { return this.oldDashboardsWithUnsavedChanges; } catch (e) { - this.notifications.toasts.addDanger({ + coreServices.notifications.toasts.addDanger({ title: backupServiceStrings.getPanelsGetError(e.message), 'data-test-subj': 'dashboardPanelsGetFailure', }); @@ -191,9 +193,11 @@ class DashboardBackupService implements DashboardBackupServiceType { } } -export const dashboardBackupServiceFactory: DashboardBackupServiceFactory = ( - core, - requiredServices -) => { - return new DashboardBackupService(requiredServices); +let dashboardBackupService: DashboardBackupService; + +export const getDashboardBackupService = () => { + if (!dashboardBackupService) { + dashboardBackupService = new DashboardBackupService(); + } + return dashboardBackupService; }; diff --git a/src/plugins/dashboard/public/services/dashboard_capabilities/dashboard_capabilities.stub.ts b/src/plugins/dashboard/public/services/dashboard_capabilities/dashboard_capabilities.stub.ts deleted file mode 100644 index 118f649d7030b..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_capabilities/dashboard_capabilities.stub.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardCapabilitiesService } from './types'; - -const defaultDashboardCapabilities: DashboardCapabilitiesService = { - show: true, - createNew: true, - saveQuery: true, - createShortUrl: true, - showWriteControls: true, - storeSearchSession: true, - mapsCapabilities: { save: true }, - visualizeCapabilities: { save: true }, -}; - -type DashboardCapabilitiesServiceFactory = PluginServiceFactory; - -export const dashboardCapabilitiesServiceFactory: DashboardCapabilitiesServiceFactory = () => { - return { - ...defaultDashboardCapabilities, - }; -}; diff --git a/src/plugins/dashboard/public/services/dashboard_capabilities/types.ts b/src/plugins/dashboard/public/services/dashboard_capabilities/types.ts deleted file mode 100644 index 4f78fa8ad38b9..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_capabilities/types.ts +++ /dev/null @@ -1,19 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export interface DashboardCapabilitiesService { - show: boolean; - saveQuery: boolean; - createNew: boolean; - mapsCapabilities: { save: boolean }; - createShortUrl: boolean; - showWriteControls: boolean; - visualizeCapabilities: { save: boolean }; - storeSearchSession: boolean; -} diff --git a/src/plugins/dashboard/public/services/dashboard_content_insights/dashboard_content_insights.stub.ts b/src/plugins/dashboard/public/services/dashboard_content_insights/dashboard_content_insights.stub.ts deleted file mode 100644 index 83ddf74df0a8a..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_content_insights/dashboard_content_insights.stub.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardContentInsightsService } from './types'; - -type DashboardContentInsightsServiceFactory = PluginServiceFactory; - -export const dashboardContentInsightsServiceFactory: DashboardContentInsightsServiceFactory = - () => { - return { - trackDashboardView: jest.fn(), - contentInsightsClient: { - track: jest.fn(), - getStats: jest.fn(), - }, - }; - }; diff --git a/src/plugins/dashboard/public/services/dashboard_content_insights/dashboard_content_insights_service.ts b/src/plugins/dashboard/public/services/dashboard_content_insights/dashboard_content_insights_service.ts deleted file mode 100644 index 349d78bb2abb3..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_content_insights/dashboard_content_insights_service.ts +++ /dev/null @@ -1,34 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { ContentInsightsClient } from '@kbn/content-management-content-insights-public'; -import { DashboardStartDependencies } from '../../plugin'; -import { DashboardContentInsightsService } from './types'; - -export type DashboardContentInsightsServiceFactory = KibanaPluginServiceFactory< - DashboardContentInsightsService, - DashboardStartDependencies ->; - -export const dashboardContentInsightsServiceFactory: DashboardContentInsightsServiceFactory = ( - params -) => { - const contentInsightsClient = new ContentInsightsClient( - { http: params.coreStart.http }, - { domainId: 'dashboard' } - ); - - return { - trackDashboardView: (dashboardId: string) => { - contentInsightsClient.track(dashboardId, 'viewed'); - }, - contentInsightsClient, - }; -}; diff --git a/src/plugins/dashboard/public/services/dashboard_content_insights/types.ts b/src/plugins/dashboard/public/services/dashboard_content_insights/types.ts deleted file mode 100644 index 848f51b920270..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_content_insights/types.ts +++ /dev/null @@ -1,15 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { ContentInsightsClientPublic } from '@kbn/content-management-content-insights-public'; - -export interface DashboardContentInsightsService { - trackDashboardView: (dashboardId: string) => void; - contentInsightsClient: ContentInsightsClientPublic; -} diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management.stub.ts b/src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management.stub.ts deleted file mode 100644 index 04adeb88c3b94..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management.stub.ts +++ /dev/null @@ -1,80 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; - -import { DashboardContentManagementService, LoadDashboardReturn } from './types'; -import { DashboardAttributes } from '../../../common/content_management'; -import { SearchDashboardsResponse } from './lib/find_dashboards'; - -export type DashboardContentManagementServiceFactory = - PluginServiceFactory; - -export const dashboardContentManagementServiceFactory: DashboardContentManagementServiceFactory = - () => { - return { - loadDashboardState: jest.fn().mockImplementation(() => - Promise.resolve({ - dashboardInput: {}, - } as LoadDashboardReturn) - ), - saveDashboardState: jest.fn(), - findDashboards: { - search: jest.fn().mockImplementation(({ search, size }) => { - const sizeToUse = size ?? 10; - const hits: SearchDashboardsResponse['hits'] = []; - for (let i = 0; i < sizeToUse; i++) { - hits.push({ - type: 'dashboard', - id: `dashboard${i}`, - attributes: { - description: `dashboard${i} desc`, - title: `dashboard${i} - ${search} - title`, - }, - references: [] as SearchDashboardsResponse['hits'][0]['references'], - } as SearchDashboardsResponse['hits'][0]); - } - return Promise.resolve({ - total: sizeToUse, - hits, - }); - }), - findById: jest.fn(), - findByIds: jest.fn().mockImplementation(() => - Promise.resolve([ - { - id: `dashboardUnsavedOne`, - status: 'success', - attributes: { - title: `Dashboard Unsaved One`, - } as unknown as DashboardAttributes, - }, - { - id: `dashboardUnsavedTwo`, - status: 'success', - attributes: { - title: `Dashboard Unsaved Two`, - } as unknown as DashboardAttributes, - }, - { - id: `dashboardUnsavedThree`, - status: 'success', - attributes: { - title: `Dashboard Unsaved Three`, - } as unknown as DashboardAttributes, - }, - ]) - ), - findByTitle: jest.fn(), - }, - deleteDashboards: jest.fn(), - checkForDuplicateDashboardTitle: jest.fn(), - updateDashboardMeta: jest.fn(), - }; - }; diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management_service.ts b/src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management_service.ts deleted file mode 100644 index 5ab0f97d3b147..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management_service.ts +++ /dev/null @@ -1,101 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; - -import type { DashboardStartDependencies } from '../../plugin'; -import { checkForDuplicateDashboardTitle } from './lib/check_for_duplicate_dashboard_title'; - -import { - searchDashboards, - findDashboardById, - findDashboardsByIds, - findDashboardIdByTitle, -} from './lib/find_dashboards'; -import { saveDashboardState } from './lib/save_dashboard_state'; -import type { - DashboardContentManagementRequiredServices, - DashboardContentManagementService, -} from './types'; -import { deleteDashboards } from './lib/delete_dashboards'; -import { loadDashboardState } from './lib/load_dashboard_state'; -import { updateDashboardMeta } from './lib/update_dashboard_meta'; -import { DashboardContentManagementCache } from './dashboard_content_management_cache'; - -export type DashboardContentManagementServiceFactory = KibanaPluginServiceFactory< - DashboardContentManagementService, - DashboardStartDependencies, - DashboardContentManagementRequiredServices ->; - -export const dashboardContentManagementCache = new DashboardContentManagementCache(); - -export const dashboardContentManagementServiceFactory: DashboardContentManagementServiceFactory = ( - { startPlugins: { contentManagement } }, - requiredServices -) => { - const { - data, - embeddable, - notifications, - dashboardBackup, - initializerContext, - savedObjectsTagging, - } = requiredServices; - return { - loadDashboardState: ({ id }) => - loadDashboardState({ - id, - data, - embeddable, - contentManagement, - savedObjectsTagging, - }), - saveDashboardState: ({ - controlGroupReferences, - currentState, - saveOptions, - lastSavedId, - panelReferences, - }) => - saveDashboardState({ - controlGroupReferences, - data, - embeddable, - saveOptions, - lastSavedId, - currentState, - notifications, - panelReferences, - dashboardBackup, - contentManagement, - initializerContext, - savedObjectsTagging, - }), - findDashboards: { - search: ({ hasReference, hasNoReference, search, size, options }) => - searchDashboards({ - contentManagement, - hasNoReference, - hasReference, - options, - search, - size, - }), - findById: (id) => findDashboardById(contentManagement, id), - findByIds: (ids) => findDashboardsByIds(contentManagement, ids), - findByTitle: (title) => findDashboardIdByTitle(contentManagement, title), - }, - checkForDuplicateDashboardTitle: (props) => - checkForDuplicateDashboardTitle(props, contentManagement), - deleteDashboards: (ids) => deleteDashboards(ids, contentManagement), - updateDashboardMeta: (props) => - updateDashboardMeta(props, { contentManagement, savedObjectsTagging, embeddable }), - }; -}; diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management_cache.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/dashboard_content_management_cache.ts similarity index 100% rename from src/plugins/dashboard/public/services/dashboard_content_management/dashboard_content_management_cache.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/dashboard_content_management_cache.ts diff --git a/src/plugins/dashboard/public/services/dashboard_content_management_service/index.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/index.ts new file mode 100644 index 0000000000000..ebc5a414f9f64 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { DashboardContentManagementCache } from './dashboard_content_management_cache'; +import { checkForDuplicateDashboardTitle } from './lib/check_for_duplicate_dashboard_title'; +import { deleteDashboards } from './lib/delete_dashboards'; +import { + findDashboardById, + findDashboardIdByTitle, + findDashboardsByIds, + searchDashboards, +} from './lib/find_dashboards'; +import { loadDashboardState } from './lib/load_dashboard_state'; +import { saveDashboardState } from './lib/save_dashboard_state'; +import { updateDashboardMeta } from './lib/update_dashboard_meta'; + +let dashboardContentManagementCache: DashboardContentManagementCache; + +export const getDashboardContentManagementCache = () => { + if (!dashboardContentManagementCache) + dashboardContentManagementCache = new DashboardContentManagementCache(); + return dashboardContentManagementCache; +}; + +export const getDashboardContentManagementService = () => { + return { + loadDashboardState, + saveDashboardState, + findDashboards: { + search: searchDashboards, + findById: findDashboardById, + findByIds: findDashboardsByIds, + findByTitle: findDashboardIdByTitle, + }, + checkForDuplicateDashboardTitle, + deleteDashboards, + updateDashboardMeta, + }; +}; diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.test.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/check_for_duplicate_dashboard_title.test.ts similarity index 64% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.test.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/check_for_duplicate_dashboard_title.test.ts index 68de161325771..c20ba72d41472 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.test.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/check_for_duplicate_dashboard_title.test.ts @@ -7,17 +7,11 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { ContentClient } from '@kbn/content-management-plugin/public'; import { checkForDuplicateDashboardTitle } from './check_for_duplicate_dashboard_title'; import { extractTitleAndCount } from '../../../dashboard_container/embeddable/api/lib/extract_title_and_count'; - -type ContentManagementStart = Parameters[1]; +import { contentManagementService } from '../../kibana_services'; describe('checkForDuplicateDashboardTitle', () => { - const mockedContentManagementClient = { - search: jest.fn(), - } as unknown as ContentClient; - const newTitle = 'Shiny dashboard (1)'; afterEach(() => { @@ -35,9 +29,7 @@ describe('checkForDuplicateDashboardTitle', () => { }, ]; - ( - mockedContentManagementClient.search as jest.MockedFunction - ).mockImplementationOnce(() => + contentManagementService.client.search = jest.fn().mockImplementationOnce(() => Promise.resolve({ hits: pageResults, pagination: { @@ -46,17 +38,14 @@ describe('checkForDuplicateDashboardTitle', () => { }) ); - await checkForDuplicateDashboardTitle( - { - title: newTitle, - lastSavedTitle: baseDashboardName, - copyOnSave: true, - isTitleDuplicateConfirmed: false, - }, - { client: mockedContentManagementClient } as ContentManagementStart - ); + await checkForDuplicateDashboardTitle({ + title: newTitle, + lastSavedTitle: baseDashboardName, + copyOnSave: true, + isTitleDuplicateConfirmed: false, + }); - expect(mockedContentManagementClient.search).toHaveBeenCalledWith( + expect(contentManagementService.client.search).toHaveBeenCalledWith( expect.objectContaining({ query: expect.objectContaining({ text: `${baseDashboardName}*`, @@ -86,9 +75,7 @@ describe('checkForDuplicateDashboardTitle', () => { const onTitleDuplicate = jest.fn(); - ( - mockedContentManagementClient.search as jest.MockedFunction - ).mockImplementationOnce(() => + contentManagementService.client.search = jest.fn().mockImplementationOnce(() => Promise.resolve({ hits: pageResults, pagination: { @@ -97,18 +84,15 @@ describe('checkForDuplicateDashboardTitle', () => { }) ); - await checkForDuplicateDashboardTitle( - { - title: userTitleInput, - lastSavedTitle: baseDashboardName, - copyOnSave: true, - isTitleDuplicateConfirmed: false, - onTitleDuplicate, - }, - { client: mockedContentManagementClient } as ContentManagementStart - ); + await checkForDuplicateDashboardTitle({ + title: userTitleInput, + lastSavedTitle: baseDashboardName, + copyOnSave: true, + isTitleDuplicateConfirmed: false, + onTitleDuplicate, + }); - expect(mockedContentManagementClient.search).toHaveBeenCalledWith( + expect(contentManagementService.client.search).toHaveBeenCalledWith( expect.objectContaining({ query: expect.objectContaining({ text: 'Shiny dashboard*', diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/check_for_duplicate_dashboard_title.ts similarity index 86% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/check_for_duplicate_dashboard_title.ts index 2d5a4f7742e9d..0d12cb446129b 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/check_for_duplicate_dashboard_title.ts @@ -7,10 +7,10 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DashboardStartDependencies } from '../../../plugin'; import { DASHBOARD_CONTENT_ID } from '../../../dashboard_constants'; import { DashboardCrudTypes } from '../../../../common/content_management'; import { extractTitleAndCount } from '../../../dashboard_container/embeddable/api/lib/extract_title_and_count'; +import { contentManagementService } from '../../kibana_services'; export interface DashboardDuplicateTitleCheckProps { title: string; @@ -28,16 +28,13 @@ export interface DashboardDuplicateTitleCheckProps { * returns Promise when there is no duplicate, or runs the provided onTitleDuplicate * function when the title already exists */ -export async function checkForDuplicateDashboardTitle( - { - title, - copyOnSave, - lastSavedTitle, - onTitleDuplicate, - isTitleDuplicateConfirmed, - }: DashboardDuplicateTitleCheckProps, - contentManagement: DashboardStartDependencies['contentManagement'] -): Promise { +export async function checkForDuplicateDashboardTitle({ + title, + copyOnSave, + lastSavedTitle, + onTitleDuplicate, + isTitleDuplicateConfirmed, +}: DashboardDuplicateTitleCheckProps): Promise { // Don't check if the title is an empty string if (!title) { return true; @@ -56,7 +53,7 @@ export async function checkForDuplicateDashboardTitle( const [baseDashboardName] = extractTitleAndCount(title); - const { hits } = await contentManagement.client.search< + const { hits } = await contentManagementService.client.search< DashboardCrudTypes['SearchIn'], DashboardCrudTypes['SearchOut'] >({ diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/dashboard_versioning.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/dashboard_versioning.ts similarity index 100% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/dashboard_versioning.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/dashboard_versioning.ts diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/delete_dashboards.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/delete_dashboards.ts similarity index 68% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/delete_dashboards.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/delete_dashboards.ts index dbafbe7382124..0be9355ddb606 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/delete_dashboards.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/delete_dashboards.ts @@ -7,18 +7,15 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DashboardStartDependencies } from '../../../plugin'; -import { DASHBOARD_CONTENT_ID } from '../../../dashboard_constants'; +import { getDashboardContentManagementCache } from '..'; import { DashboardCrudTypes } from '../../../../common/content_management'; -import { dashboardContentManagementCache } from '../dashboard_content_management_service'; +import { DASHBOARD_CONTENT_ID } from '../../../dashboard_constants'; +import { contentManagementService } from '../../kibana_services'; -export const deleteDashboards = async ( - ids: string[], - contentManagement: DashboardStartDependencies['contentManagement'] -) => { +export const deleteDashboards = async (ids: string[]) => { const deletePromises = ids.map((id) => { - dashboardContentManagementCache.deleteDashboard(id); - return contentManagement.client.delete< + getDashboardContentManagementCache().deleteDashboard(id); + return contentManagementService.client.delete< DashboardCrudTypes['DeleteIn'], DashboardCrudTypes['DeleteOut'] >({ diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/find_dashboards.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/find_dashboards.ts similarity index 78% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/find_dashboards.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/find_dashboards.ts index eef3e9e142040..2f9a2c2e9a033 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/find_dashboards.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/find_dashboards.ts @@ -10,17 +10,16 @@ import type { Reference } from '@kbn/content-management-utils'; import { SavedObjectError, SavedObjectsFindOptionsReference } from '@kbn/core/public'; +import { getDashboardContentManagementCache } from '..'; import { DashboardAttributes, DashboardCrudTypes, DashboardItem, } from '../../../../common/content_management'; import { DASHBOARD_CONTENT_ID } from '../../../dashboard_constants'; -import { DashboardStartDependencies } from '../../../plugin'; -import { dashboardContentManagementCache } from '../dashboard_content_management_service'; +import { contentManagementService } from '../../kibana_services'; export interface SearchDashboardsArgs { - contentManagement: DashboardStartDependencies['contentManagement']; options?: DashboardCrudTypes['SearchIn']['options']; hasNoReference?: SavedObjectsFindOptionsReference[]; hasReference?: SavedObjectsFindOptionsReference[]; @@ -34,7 +33,6 @@ export interface SearchDashboardsResponse { } export async function searchDashboards({ - contentManagement, hasNoReference, hasReference, options, @@ -44,7 +42,7 @@ export async function searchDashboards({ const { hits, pagination: { total }, - } = await contentManagement.client.search< + } = await contentManagementService.client.search< DashboardCrudTypes['SearchIn'], DashboardCrudTypes['SearchOut'] >({ @@ -70,10 +68,9 @@ export type FindDashboardsByIdResponse = { id: string } & ( | { status: 'error'; error: SavedObjectError } ); -export async function findDashboardById( - contentManagement: DashboardStartDependencies['contentManagement'], - id: string -): Promise { +export async function findDashboardById(id: string): Promise { + const dashboardContentManagementCache = getDashboardContentManagementCache(); + /** If the dashboard exists in the cache, then return the result from that */ const cachedDashboard = dashboardContentManagementCache.fetchDashboard(id); if (cachedDashboard) { @@ -87,7 +84,7 @@ export async function findDashboardById( /** Otherwise, fetch the dashboard from the content management client, add it to the cache, and return the result */ try { - const response = await contentManagement.client.get< + const response = await contentManagementService.client.get< DashboardCrudTypes['GetIn'], DashboardCrudTypes['GetOut'] >({ @@ -114,20 +111,14 @@ export async function findDashboardById( } } -export async function findDashboardsByIds( - contentManagement: DashboardStartDependencies['contentManagement'], - ids: string[] -): Promise { - const findPromises = ids.map((id) => findDashboardById(contentManagement, id)); +export async function findDashboardsByIds(ids: string[]): Promise { + const findPromises = ids.map((id) => findDashboardById(id)); const results = await Promise.all(findPromises); return results as FindDashboardsByIdResponse[]; } -export async function findDashboardIdByTitle( - contentManagement: DashboardStartDependencies['contentManagement'], - title: string -): Promise<{ id: string } | undefined> { - const { hits } = await contentManagement.client.search< +export async function findDashboardIdByTitle(title: string): Promise<{ id: string } | undefined> { + const { hits } = await contentManagementService.client.search< DashboardCrudTypes['SearchIn'], DashboardCrudTypes['SearchOut'] >({ diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/load_dashboard_state.test.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.test.ts similarity index 76% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/load_dashboard_state.test.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.test.ts index 0f6880cb76e8a..e03a078e0df45 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/load_dashboard_state.test.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.test.ts @@ -7,23 +7,14 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { registry } from '../../plugin_services.stub'; -import { pluginServices } from '../../plugin_services'; +import { getDashboardContentManagementCache } from '..'; import { getSampleDashboardInput } from '../../../mocks'; +import { contentManagementService } from '../../kibana_services'; import { loadDashboardState } from './load_dashboard_state'; -import { dashboardContentManagementCache } from '../dashboard_content_management_service'; - -pluginServices.setRegistry(registry.start({})); -const { data, embeddable, contentManagement, savedObjectsTagging } = pluginServices.getServices(); - -const allServices = { - data, - embeddable, - contentManagement, - savedObjectsTagging, -}; describe('Load dashboard state', () => { + const dashboardContentManagementCache = getDashboardContentManagementCache(); + it('should return cached result if available', async () => { dashboardContentManagementCache.fetchDashboard = jest.fn().mockImplementation((id: string) => { return { @@ -40,17 +31,16 @@ describe('Load dashboard state', () => { meta: {}, }; }); + contentManagementService.client.get = jest.fn(); dashboardContentManagementCache.addDashboard = jest.fn(); - contentManagement.client.get = jest.fn(); const { id } = getSampleDashboardInput(); const result = await loadDashboardState({ id, - ...allServices, }); expect(dashboardContentManagementCache.fetchDashboard).toBeCalled(); expect(dashboardContentManagementCache.addDashboard).not.toBeCalled(); - expect(contentManagement.client.get).not.toBeCalled(); + expect(contentManagementService.client.get).not.toBeCalled(); expect(result).toMatchObject({ dashboardId: id, dashboardFound: true, @@ -63,7 +53,7 @@ describe('Load dashboard state', () => { it('should not add to cache for alias redirect result', async () => { dashboardContentManagementCache.fetchDashboard = jest.fn().mockImplementation(() => undefined); dashboardContentManagementCache.addDashboard = jest.fn(); - contentManagement.client.get = jest.fn().mockImplementation(({ id }) => { + contentManagementService.client.get = jest.fn().mockImplementation(({ id }) => { return Promise.resolve({ item: { id }, meta: { @@ -74,7 +64,6 @@ describe('Load dashboard state', () => { const { id } = getSampleDashboardInput(); await loadDashboardState({ id, - ...allServices, }); expect(dashboardContentManagementCache.fetchDashboard).toBeCalled(); expect(dashboardContentManagementCache.addDashboard).not.toBeCalled(); diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/load_dashboard_state.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.ts similarity index 84% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/load_dashboard_state.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.ts index 2f78325e083f5..17102e2fe7d0a 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/load_dashboard_state.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.ts @@ -7,26 +7,32 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { v4 as uuidv4 } from 'uuid'; import { has } from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; -import { Filter, Query } from '@kbn/es-query'; +import { injectSearchSourceReferences, parseSearchSourceJSON } from '@kbn/data-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { Filter, Query } from '@kbn/es-query'; import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/public'; import { cleanFiltersForSerialize } from '@kbn/presentation-util-plugin/public'; -import { parseSearchSourceJSON, injectSearchSourceReferences } from '@kbn/data-plugin/public'; +import { getDashboardContentManagementCache } from '..'; import { + convertSavedPanelsToPanelMap, injectReferences, type DashboardOptions, - convertSavedPanelsToPanelMap, } from '../../../../common'; -import { migrateDashboardInput } from './migrate_dashboard_input'; -import { convertNumberToDashboardVersion } from './dashboard_versioning'; import { DashboardCrudTypes } from '../../../../common/content_management'; -import type { LoadDashboardFromSavedObjectProps, LoadDashboardReturn } from '../types'; -import { dashboardContentManagementCache } from '../dashboard_content_management_service'; import { DASHBOARD_CONTENT_ID, DEFAULT_DASHBOARD_INPUT } from '../../../dashboard_constants'; +import { + contentManagementService, + dataService, + embeddableService, + savedObjectsTaggingService, +} from '../../kibana_services'; +import type { LoadDashboardFromSavedObjectProps, LoadDashboardReturn } from '../types'; +import { convertNumberToDashboardVersion } from './dashboard_versioning'; +import { migrateDashboardInput } from './migrate_dashboard_input'; export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { // Lucene was the only option before, so language-less queries are all lucene @@ -39,15 +45,12 @@ export function migrateLegacyQuery(query: Query | { [key: string]: any } | strin export const loadDashboardState = async ({ id, - data, - embeddable, - contentManagement, - savedObjectsTagging, }: LoadDashboardFromSavedObjectProps): Promise => { const { search: dataSearchService, query: { queryString }, - } = data; + } = dataService; + const dashboardContentManagementCache = getDashboardContentManagementCache(); const savedObjectId = id; const embeddableId = uuidv4(); @@ -78,7 +81,7 @@ export const loadDashboardState = async ({ ({ item: rawDashboardContent, meta: resolveMeta } = cachedDashboard); } else { /** Otherwise, fetch and load the dashboard from the content management client, and add it to the cache */ - const result = await contentManagement.client + const result = await contentManagementService.client .get({ contentTypeId: DASHBOARD_CONTENT_ID, id, @@ -116,7 +119,7 @@ export const loadDashboardState = async ({ return injectReferences( { references, attributes: rawAttributes }, { - embeddablePersistableStateService: embeddable, + embeddablePersistableStateService: embeddableService, } ); })(); @@ -170,30 +173,27 @@ export const loadDashboardState = async ({ const options: DashboardOptions = optionsJSON ? JSON.parse(optionsJSON) : undefined; const panels = convertSavedPanelsToPanelMap(panelsJSON ? JSON.parse(panelsJSON) : []); - const { dashboardInput, anyMigrationRun } = migrateDashboardInput( - { - ...DEFAULT_DASHBOARD_INPUT, - ...options, - - id: embeddableId, - refreshInterval, - timeRestore, - description, - timeRange, - filters, - panels, - query, - title, - - viewMode: ViewMode.VIEW, // dashboards loaded from saved object default to view mode. If it was edited recently, the view mode from session storage will override this. - tags: savedObjectsTagging.getTagIdsFromReferences?.(references) ?? [], - - controlGroupInput: attributes.controlGroupInput, - - version: convertNumberToDashboardVersion(version), - }, - embeddable - ); + const { dashboardInput, anyMigrationRun } = migrateDashboardInput({ + ...DEFAULT_DASHBOARD_INPUT, + ...options, + + id: embeddableId, + refreshInterval, + timeRestore, + description, + timeRange, + filters, + panels, + query, + title, + + viewMode: ViewMode.VIEW, // dashboards loaded from saved object default to view mode. If it was edited recently, the view mode from session storage will override this. + tags: savedObjectsTaggingService?.getTaggingApi()?.ui.getTagIdsFromReferences(references) ?? [], + + controlGroupInput: attributes.controlGroupInput, + + version: convertNumberToDashboardVersion(version), + }); return { managed, diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/migrate_dashboard_input.test.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/migrate_dashboard_input.test.ts similarity index 84% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/migrate_dashboard_input.test.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/migrate_dashboard_input.test.ts index 4b84d67a4bcaf..ad0a5cc386b26 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/migrate_dashboard_input.test.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/migrate_dashboard_input.test.ts @@ -8,7 +8,7 @@ */ import { getSampleDashboardInput, getSampleDashboardPanel } from '../../../mocks'; -import { DashboardEmbeddableService } from '../../embeddable/types'; +import { embeddableService } from '../../kibana_services'; import { SavedDashboardInput } from '../types'; import { migrateDashboardInput } from './migrate_dashboard_input'; @@ -31,14 +31,12 @@ describe('Migrate dashboard input', () => { panel4: getSampleDashboardPanel({ type: 'ultraDiscover', explicitInput: { id: 'panel4' } }), }; - const embeddableService: DashboardEmbeddableService = { - getEmbeddableFactory: jest.fn(() => ({ - latestVersion: '1.0.0', - migrations: {}, - })), - } as unknown as DashboardEmbeddableService; + embeddableService.getEmbeddableFactory = jest.fn(() => ({ + latestVersion: '1.0.0', + migrations: {}, + })) as unknown as typeof embeddableService.getEmbeddableFactory; - const result = migrateDashboardInput(dashboardInput, embeddableService); + const result = migrateDashboardInput(dashboardInput); // migration run should be true because the runEmbeddableFactoryMigrations mock above returns true. expect(result.anyMigrationRun).toBe(true); diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/migrate_dashboard_input.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/migrate_dashboard_input.ts similarity index 83% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/migrate_dashboard_input.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/migrate_dashboard_input.ts index cfae6b1fa809a..b0cfac9f97ce4 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/migrate_dashboard_input.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/migrate_dashboard_input.ts @@ -12,8 +12,7 @@ import { runEmbeddableFactoryMigrations, } from '@kbn/embeddable-plugin/public'; import { DashboardContainerInput, DashboardPanelState } from '../../../../common'; -import { type DashboardEmbeddableService } from '../../embeddable/types'; -import { pluginServices } from '../../plugin_services'; +import { embeddableService } from '../../kibana_services'; import { SavedDashboardInput } from '../types'; /** @@ -22,25 +21,19 @@ import { SavedDashboardInput } from '../types'; * This prevents the reset button from un-migrating the panels on the Dashboard. This also means that the migrations may * get skipped at Embeddable create time - unless states with older versions are saved in the URL or session storage. */ -export const migrateDashboardInput = ( - dashboardInput: SavedDashboardInput, - embeddable: DashboardEmbeddableService -) => { - const { - embeddable: { reactEmbeddableRegistryHasKey }, - } = pluginServices.getServices(); +export const migrateDashboardInput = (dashboardInput: SavedDashboardInput) => { let anyMigrationRun = false; if (!dashboardInput) return dashboardInput; const migratedPanels: DashboardContainerInput['panels'] = {}; for (const [id, panel] of Object.entries(dashboardInput.panels)) { // if the panel type is registered in the new embeddable system, we do not need to run migrations for it. - if (reactEmbeddableRegistryHasKey(panel.type)) { + if (embeddableService.reactEmbeddableRegistryHasKey(panel.type)) { migratedPanels[id] = panel; continue; } - const factory = embeddable.getEmbeddableFactory(panel.type); + const factory = embeddableService.getEmbeddableFactory(panel.type); if (!factory) throw new EmbeddableFactoryNotFoundError(panel.type); // run last saved migrations for by value panels only. if (!panel.explicitInput.savedObjectId) { diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.test.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/save_dashboard_state.test.ts similarity index 75% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.test.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/save_dashboard_state.test.ts index 342dd0bf6f8de..8327397a66068 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.test.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/save_dashboard_state.test.ts @@ -7,24 +7,17 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { registry } from '../../plugin_services.stub'; -import { pluginServices } from '../../plugin_services'; +import { DashboardContainerInput } from '../../../../common'; import { getSampleDashboardInput } from '../../../mocks'; +import { + contentManagementService, + coreServices, + dataService, + embeddableService, +} from '../../kibana_services'; import { saveDashboardState } from './save_dashboard_state'; -import { DashboardContainerInput } from '../../../../common'; -pluginServices.setRegistry(registry.start({})); -const { - data, - embeddable, - notifications, - dashboardBackup, - contentManagement, - initializerContext, - savedObjectsTagging, -} = pluginServices.getServices(); - -contentManagement.client.create = jest.fn().mockImplementation(({ options }) => { +contentManagementService.client.create = jest.fn().mockImplementation(({ options }) => { if (options.id === undefined) { return { item: { id: 'newlyGeneratedId' } }; } @@ -32,24 +25,18 @@ contentManagement.client.create = jest.fn().mockImplementation(({ options }) => throw new Error('Update should be used when id is provided'); }); -contentManagement.client.update = jest.fn().mockImplementation(({ id }) => { +contentManagementService.client.update = jest.fn().mockImplementation(({ id }) => { if (id === undefined) { throw new Error('Update needs an id'); } return { item: { id } }; }); -const allServices = { - data, - embeddable, - notifications, - dashboardBackup, - contentManagement, - initializerContext, - savedObjectsTagging, -}; -data.query.timefilter.timefilter.getTime = jest.fn().mockReturnValue({ from: 'then', to: 'now' }); -embeddable.extract = jest +dataService.query.timefilter.timefilter.getTime = jest + .fn() + .mockReturnValue({ from: 'then', to: 'now' }); + +embeddableService.extract = jest .fn() .mockImplementation((attributes) => ({ state: attributes, references: [] })); @@ -66,14 +53,13 @@ describe('Save dashboard state', () => { } as unknown as DashboardContainerInput, lastSavedId: 'Boogaloo', saveOptions: {}, - ...allServices, }); expect(result.id).toBe('Boogaloo'); - expect(allServices.contentManagement.client.update).toHaveBeenCalledWith( + expect(contentManagementService.client.update).toHaveBeenCalledWith( expect.objectContaining({ id: 'Boogaloo' }) ); - expect(allServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ + expect(coreServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: `Dashboard 'BOO' was saved`, className: 'eui-textBreakWord', 'data-test-subj': 'saveDashboardSuccess', @@ -88,17 +74,16 @@ describe('Save dashboard state', () => { } as unknown as DashboardContainerInput, lastSavedId: 'Boogaloonie', saveOptions: { saveAsCopy: true }, - ...allServices, }); expect(result.id).toBe('newlyGeneratedId'); expect(result.redirectRequired).toBe(true); - expect(allServices.contentManagement.client.create).toHaveBeenCalledWith( + expect(contentManagementService.client.create).toHaveBeenCalledWith( expect.objectContaining({ options: { references: [] }, }) ); - expect(allServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ + expect(coreServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: `Dashboard 'BooToo' was saved`, className: 'eui-textBreakWord', 'data-test-subj': 'saveDashboardSuccess', @@ -114,12 +99,11 @@ describe('Save dashboard state', () => { } as unknown as DashboardContainerInput, lastSavedId: 'Boogatoonie', saveOptions: { saveAsCopy: true }, - ...allServices, }); expect(result.id).toBe('newlyGeneratedId'); - expect(allServices.contentManagement.client.create).toHaveBeenCalledWith( + expect(contentManagementService.client.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ panelsJSON: expect.not.stringContaining('neverGonnaGetThisId'), @@ -138,11 +122,10 @@ describe('Save dashboard state', () => { panelReferences: [{ name: 'idOne:panel_idOne', type: 'boop', id: 'idOne' }], lastSavedId: 'Boogatoonie', saveOptions: { saveAsCopy: true }, - ...allServices, }); expect(result.id).toBe('newlyGeneratedId'); - expect(allServices.contentManagement.client.create).toHaveBeenCalledWith( + expect(contentManagementService.client.create).toHaveBeenCalledWith( expect.objectContaining({ options: expect.objectContaining({ references: expect.arrayContaining([ @@ -157,7 +140,7 @@ describe('Save dashboard state', () => { }); it('should return an error when the save fails.', async () => { - contentManagement.client.create = jest.fn().mockRejectedValue('Whoops'); + contentManagementService.client.create = jest.fn().mockRejectedValue('Whoops'); const result = await saveDashboardState({ currentState: { ...getSampleDashboardInput(), @@ -166,7 +149,6 @@ describe('Save dashboard state', () => { } as unknown as DashboardContainerInput, lastSavedId: 'Boogatoonie', saveOptions: { saveAsCopy: true }, - ...allServices, }); expect(result.id).toBeUndefined(); diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/save_dashboard_state.ts similarity index 82% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/save_dashboard_state.ts index b9285b208c14d..283ed5eed7f5b 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/save_dashboard_state.ts @@ -13,20 +13,23 @@ import moment, { Moment } from 'moment'; import { extractSearchSourceReferences, RefreshInterval } from '@kbn/data-plugin/public'; import { isFilterPinned } from '@kbn/es-query'; +import { getDashboardContentManagementCache } from '..'; import { convertPanelMapToSavedPanels, extractReferences } from '../../../../common'; import { DashboardAttributes, DashboardCrudTypes } from '../../../../common/content_management'; +import { generateNewPanelIds } from '../../../../common/lib/dashboard_panel_converters'; import { DASHBOARD_CONTENT_ID } from '../../../dashboard_constants'; import { LATEST_DASHBOARD_CONTAINER_VERSION } from '../../../dashboard_container'; import { dashboardSaveToastStrings } from '../../../dashboard_container/_dashboard_container_strings'; -import { DashboardStartDependencies } from '../../../plugin'; -import { dashboardContentManagementCache } from '../dashboard_content_management_service'; +import { getDashboardBackupService } from '../../dashboard_backup_service'; import { - DashboardContentManagementRequiredServices, - SaveDashboardProps, - SaveDashboardReturn, -} from '../types'; + contentManagementService, + coreServices, + dataService, + embeddableService, + savedObjectsTaggingService, +} from '../../kibana_services'; +import { SaveDashboardProps, SaveDashboardReturn } from '../types'; import { convertDashboardVersionToNumber } from './dashboard_versioning'; -import { generateNewPanelIds } from '../../../../common/lib/dashboard_panel_converters'; export const convertTimeToUTCString = (time?: string | Moment): undefined | string => { if (moment(time).isValid()) { @@ -38,35 +41,20 @@ export const convertTimeToUTCString = (time?: string | Moment): undefined | stri } }; -type SaveDashboardStateProps = SaveDashboardProps & { - data: DashboardContentManagementRequiredServices['data']; - contentManagement: DashboardStartDependencies['contentManagement']; - embeddable: DashboardContentManagementRequiredServices['embeddable']; - notifications: DashboardContentManagementRequiredServices['notifications']; - dashboardBackup: DashboardContentManagementRequiredServices['dashboardBackup']; - initializerContext: DashboardContentManagementRequiredServices['initializerContext']; - savedObjectsTagging: DashboardContentManagementRequiredServices['savedObjectsTagging']; -}; - export const saveDashboardState = async ({ controlGroupReferences, - data, - embeddable, lastSavedId, saveOptions, currentState, panelReferences, - dashboardBackup, - contentManagement, - savedObjectsTagging, - notifications: { toasts }, -}: SaveDashboardStateProps): Promise => { +}: SaveDashboardProps): Promise => { const { search: dataSearchService, query: { timefilter: { timefilter }, }, - } = data; + } = dataService; + const dashboardContentManagementCache = getDashboardContentManagementCache(); const { tags, @@ -165,11 +153,12 @@ export const saveDashboardState = async ({ attributes: rawDashboardAttributes, references: searchSourceReferences, }, - { embeddablePersistableStateService: embeddable } + { embeddablePersistableStateService: embeddableService } ); - const references = savedObjectsTagging.updateTagsReferences - ? savedObjectsTagging.updateTagsReferences(dashboardReferences, tags) + const savedObjectsTaggingApi = savedObjectsTaggingService?.getTaggingApi(); + const references = savedObjectsTaggingApi?.ui.updateTagsReferences + ? savedObjectsTaggingApi?.ui.updateTagsReferences(dashboardReferences, tags) : dashboardReferences; const allReferences = [ @@ -185,7 +174,7 @@ export const saveDashboardState = async ({ try { const result = idToSaveTo - ? await contentManagement.client.update< + ? await contentManagementService.client.update< DashboardCrudTypes['UpdateIn'], DashboardCrudTypes['UpdateOut'] >({ @@ -198,7 +187,7 @@ export const saveDashboardState = async ({ mergeAttributes: false, }, }) - : await contentManagement.client.create< + : await contentManagementService.client.create< DashboardCrudTypes['CreateIn'], DashboardCrudTypes['CreateOut'] >({ @@ -208,11 +197,10 @@ export const saveDashboardState = async ({ references: allReferences, }, }); - const newId = result.item.id; if (newId) { - toasts.addSuccess({ + coreServices.notifications.toasts.addSuccess({ title: dashboardSaveToastStrings.getSuccessString(currentState.title), className: 'eui-textBreakWord', 'data-test-subj': 'saveDashboardSuccess', @@ -222,7 +210,7 @@ export const saveDashboardState = async ({ * If the dashboard id has been changed, redirect to the new ID to keep the url param in sync. */ if (newId !== lastSavedId) { - dashboardBackup.clearState(lastSavedId); + getDashboardBackupService().clearState(lastSavedId); return { redirectRequired: true, id: newId, references: allReferences }; } else { dashboardContentManagementCache.deleteDashboard(newId); // something changed in an existing dashboard, so delete it from the cache so that it can be re-fetched @@ -230,7 +218,7 @@ export const saveDashboardState = async ({ } return { id: newId, references: allReferences }; } catch (error) { - toasts.addDanger({ + coreServices.notifications.toasts.addDanger({ title: dashboardSaveToastStrings.getFailureString(currentState.title, error.message), 'data-test-subj': 'saveDashboardFailure', }); diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/update_dashboard_meta.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/update_dashboard_meta.ts similarity index 56% rename from src/plugins/dashboard/public/services/dashboard_content_management/lib/update_dashboard_meta.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/lib/update_dashboard_meta.ts index 60da204fcb073..2fd57738f17aa 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/update_dashboard_meta.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/update_dashboard_meta.ts @@ -8,37 +8,34 @@ */ import { DashboardContainerInput } from '../../../../common'; -import { DashboardStartDependencies } from '../../../plugin'; import { DASHBOARD_CONTENT_ID } from '../../../dashboard_constants'; import { DashboardCrudTypes } from '../../../../common/content_management'; import { findDashboardsByIds } from './find_dashboards'; -import { DashboardContentManagementRequiredServices } from '../types'; +import { contentManagementService, savedObjectsTaggingService } from '../../kibana_services'; type UpdateDashboardMetaProps = Pick< DashboardContainerInput, 'id' | 'title' | 'description' | 'tags' >; -interface UpdateDashboardMetaDependencies { - contentManagement: DashboardStartDependencies['contentManagement']; - savedObjectsTagging: DashboardContentManagementRequiredServices['savedObjectsTagging']; - embeddable: DashboardContentManagementRequiredServices['embeddable']; -} -export const updateDashboardMeta = async ( - { id, title, description = '', tags }: UpdateDashboardMetaProps, - { contentManagement, savedObjectsTagging, embeddable }: UpdateDashboardMetaDependencies -) => { - const [dashboard] = await findDashboardsByIds(contentManagement, [id]); +export const updateDashboardMeta = async ({ + id, + title, + description = '', + tags, +}: UpdateDashboardMetaProps) => { + const [dashboard] = await findDashboardsByIds([id]); if (dashboard.status === 'error') { return; } + const savedObjectsTaggingApi = savedObjectsTaggingService?.getTaggingApi(); const references = - savedObjectsTagging.updateTagsReferences && tags.length - ? savedObjectsTagging.updateTagsReferences(dashboard.references, tags) + savedObjectsTaggingApi?.ui.updateTagsReferences && tags.length + ? savedObjectsTaggingApi.ui.updateTagsReferences(dashboard.references, tags) : dashboard.references; - await contentManagement.client.update< + await contentManagementService.client.update< DashboardCrudTypes['UpdateIn'], DashboardCrudTypes['UpdateOut'] >({ diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/types.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/types.ts similarity index 72% rename from src/plugins/dashboard/public/services/dashboard_content_management/types.ts rename to src/plugins/dashboard/public/services/dashboard_content_management_service/types.ts index 19ddfac9e1199..3294bb06c0d42 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/types.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/types.ts @@ -8,20 +8,11 @@ */ import type { Reference } from '@kbn/content-management-utils'; +import { ControlGroupRuntimeState } from '@kbn/controls-plugin/public'; import { SavedObjectSaveOpts } from '@kbn/saved-objects-plugin/public'; -import { ControlGroupRuntimeState } from '@kbn/controls-plugin/public'; import { DashboardContainerInput } from '../../../common'; import { DashboardAttributes, DashboardCrudTypes } from '../../../common/content_management'; -import { DashboardStartDependencies } from '../../plugin'; -import { DashboardBackupServiceType } from '../dashboard_backup/types'; -import { DashboardDataService } from '../data/types'; -import { DashboardEmbeddableService } from '../embeddable/types'; -import { DashboardInitializerContextService } from '../initializer_context/types'; -import { DashboardNotificationsService } from '../notifications/types'; -import { DashboardSavedObjectsTaggingService } from '../saved_objects_tagging/types'; -import { DashboardScreenshotModeService } from '../screenshot_mode/types'; -import { DashboardSpacesService } from '../spaces/types'; import { DashboardDuplicateTitleCheckProps } from './lib/check_for_duplicate_dashboard_title'; import { FindDashboardsByIdResponse, @@ -29,17 +20,6 @@ import { SearchDashboardsResponse, } from './lib/find_dashboards'; -export interface DashboardContentManagementRequiredServices { - data: DashboardDataService; - spaces: DashboardSpacesService; - embeddable: DashboardEmbeddableService; - notifications: DashboardNotificationsService; - dashboardBackup: DashboardBackupServiceType; - screenshotMode: DashboardScreenshotModeService; - initializerContext: DashboardInitializerContextService; - savedObjectsTagging: DashboardSavedObjectsTaggingService; -} - export interface DashboardContentManagementService { findDashboards: FindDashboardsService; deleteDashboards: (ids: string[]) => Promise; @@ -56,10 +36,6 @@ export interface DashboardContentManagementService { */ export interface LoadDashboardFromSavedObjectProps { id?: string; - data: DashboardContentManagementRequiredServices['data']; - contentManagement: DashboardStartDependencies['contentManagement']; - embeddable: DashboardContentManagementRequiredServices['embeddable']; - savedObjectsTagging: DashboardContentManagementRequiredServices['savedObjectsTagging']; } type DashboardResolveMeta = DashboardCrudTypes['GetOut']['meta']; diff --git a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.stub.ts b/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.stub.ts deleted file mode 100644 index 9e06c0b06483d..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.stub.ts +++ /dev/null @@ -1,21 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { FavoritesClient } from '@kbn/content-management-favorites-public'; -import { httpServiceMock } from '@kbn/core-http-browser-mocks'; -import { DashboardFavoritesService } from './types'; - -export type DashboardFavoritesServiceFactory = PluginServiceFactory; - -export const dashboardFavoritesServiceFactory: DashboardFavoritesServiceFactory = () => { - return new FavoritesClient('dashboards', 'dashboard', { - http: httpServiceMock.createStartContract(), - }); -}; diff --git a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.ts b/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.ts deleted file mode 100644 index 503f90c11e549..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.ts +++ /dev/null @@ -1,29 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { FavoritesClient } from '@kbn/content-management-favorites-public'; -import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardStartDependencies } from '../../plugin'; -import { DashboardFavoritesService } from './types'; -import { DASHBOARD_APP_ID, DASHBOARD_CONTENT_ID } from '../../dashboard_constants'; - -export type DashboardFavoritesServiceFactory = KibanaPluginServiceFactory< - DashboardFavoritesService, - DashboardStartDependencies ->; - -export const dashboardFavoritesServiceFactory: DashboardFavoritesServiceFactory = ({ - coreStart, - startPlugins, -}) => { - return new FavoritesClient(DASHBOARD_APP_ID, DASHBOARD_CONTENT_ID, { - http: coreStart.http, - usageCollection: startPlugins.usageCollection, - }); -}; diff --git a/src/plugins/dashboard/public/services/dashboard_favorites/types.ts b/src/plugins/dashboard/public/services/dashboard_favorites/types.ts deleted file mode 100644 index 3ed4b88f48941..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_favorites/types.ts +++ /dev/null @@ -1,12 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { FavoritesClientPublic } from '@kbn/content-management-favorites-public'; - -export type DashboardFavoritesService = FavoritesClientPublic; diff --git a/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.stub.ts b/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.stub.ts deleted file mode 100644 index 8932b3210f0ae..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.stub.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardRecentlyAccessedService } from './types'; - -type DashboardRecentlyAccessedServiceFactory = - PluginServiceFactory; - -export const dashboardRecentlyAccessedServiceFactory: DashboardRecentlyAccessedServiceFactory = - () => { - return { - add: jest.fn(), - get: jest.fn(), - get$: jest.fn(), - }; - }; diff --git a/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.ts b/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.ts deleted file mode 100644 index bdfe42e9988bd..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.ts +++ /dev/null @@ -1,33 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { RecentlyAccessedService } from '@kbn/recently-accessed'; -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; - -import { DashboardHTTPService } from '../http/types'; -import { DashboardStartDependencies } from '../../plugin'; -import { DashboardRecentlyAccessedService } from './types'; - -interface DashboardRecentlyAccessedRequiredServices { - http: DashboardHTTPService; -} - -export type DashboardBackupServiceFactory = KibanaPluginServiceFactory< - DashboardRecentlyAccessedService, - DashboardStartDependencies, - DashboardRecentlyAccessedRequiredServices ->; - -export const dashboardRecentlyAccessedFactory: DashboardBackupServiceFactory = ( - core, - requiredServices -) => { - const { http } = requiredServices; - return new RecentlyAccessedService().start({ http, key: 'dashboardRecentlyAccessed' }); -}; diff --git a/src/plugins/dashboard/public/services/dashboard_recently_accessed/types.ts b/src/plugins/dashboard/public/services/dashboard_recently_accessed/types.ts deleted file mode 100644 index 96f1772bb8ee3..0000000000000 --- a/src/plugins/dashboard/public/services/dashboard_recently_accessed/types.ts +++ /dev/null @@ -1,12 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { RecentlyAccessed } from '@kbn/recently-accessed'; - -export type DashboardRecentlyAccessedService = RecentlyAccessed; diff --git a/src/plugins/dashboard/public/services/i18n/i18n.stub.ts b/src/plugins/dashboard/public/services/dashboard_recently_accessed_service.ts similarity index 50% rename from src/plugins/dashboard/public/services/i18n/i18n.stub.ts rename to src/plugins/dashboard/public/services/dashboard_recently_accessed_service.ts index d756f2632aaac..06ab02857c30a 100644 --- a/src/plugins/dashboard/public/services/i18n/i18n.stub.ts +++ b/src/plugins/dashboard/public/services/dashboard_recently_accessed_service.ts @@ -7,14 +7,17 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { coreMock } from '@kbn/core/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardI18nService } from './types'; +import { RecentlyAccessed, RecentlyAccessedService } from '@kbn/recently-accessed'; +import { coreServices } from './kibana_services'; -type I18nServiceFactory = PluginServiceFactory; +let dashboardRecentlyAccessed: RecentlyAccessed; -export const i18nServiceFactory: I18nServiceFactory = () => { - const { i18n } = coreMock.createStart(); - - return i18n; +export const getDashboardRecentlyAccessedService = () => { + if (!dashboardRecentlyAccessed) { + dashboardRecentlyAccessed = new RecentlyAccessedService().start({ + http: coreServices.http, + key: 'dashboardRecentlyAccessed', + }); + } + return dashboardRecentlyAccessed; }; diff --git a/src/plugins/dashboard/public/services/data/data.stub.ts b/src/plugins/dashboard/public/services/data/data.stub.ts deleted file mode 100644 index 9f688f4cd5dd1..0000000000000 --- a/src/plugins/dashboard/public/services/data/data.stub.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -// import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardDataService } from './types'; - -type DataServiceFactory = PluginServiceFactory; - -export const dataServiceFactory: DataServiceFactory = () => ({ - ...dataPluginMock.createStartContract(), - dataViews: { - ...dataViewPluginMocks.createStartContract(), - defaultDataViewExists: async () => true, - }, -}); diff --git a/src/plugins/dashboard/public/services/data/data_service.ts b/src/plugins/dashboard/public/services/data/data_service.ts deleted file mode 100644 index 7e464c2609c19..0000000000000 --- a/src/plugins/dashboard/public/services/data/data_service.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardDataService } from './types'; - -export type DataServiceFactory = KibanaPluginServiceFactory< - DashboardDataService, - DashboardStartDependencies ->; - -export const dataServiceFactory: DataServiceFactory = ({ startPlugins }) => { - const { - data: { dataViews, fieldFormats, query, search }, - } = startPlugins; - - return { - dataViews, - fieldFormats, - query, - search, - }; -}; diff --git a/src/plugins/dashboard/public/services/data/types.ts b/src/plugins/dashboard/public/services/data/types.ts deleted file mode 100644 index 81e42a455095d..0000000000000 --- a/src/plugins/dashboard/public/services/data/types.ts +++ /dev/null @@ -1,17 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; - -export interface DashboardDataService { - dataViews: DataPublicPluginStart['dataViews']; - fieldFormats: DataPublicPluginStart['fieldFormats']; - query: DataPublicPluginStart['query']; - search: DataPublicPluginStart['search']; -} diff --git a/src/plugins/dashboard/public/services/data_view_editor/data_view_editor.stub.ts b/src/plugins/dashboard/public/services/data_view_editor/data_view_editor.stub.ts deleted file mode 100644 index b5d15dafbad43..0000000000000 --- a/src/plugins/dashboard/public/services/data_view_editor/data_view_editor.stub.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardDataViewEditorService } from './types'; - -type DataViewEditorServiceFactory = PluginServiceFactory; - -export const dataViewEditorServiceFactory: DataViewEditorServiceFactory = () => { - const dataViewEditorMock = dataViewEditorPluginMock.createStartContract(); - - return { - openEditor: dataViewEditorMock.openEditor, - userPermissions: dataViewEditorMock.userPermissions, - }; -}; diff --git a/src/plugins/dashboard/public/services/data_view_editor/data_view_editor_service.ts b/src/plugins/dashboard/public/services/data_view_editor/data_view_editor_service.ts deleted file mode 100644 index d64eba5d32821..0000000000000 --- a/src/plugins/dashboard/public/services/data_view_editor/data_view_editor_service.ts +++ /dev/null @@ -1,28 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardDataViewEditorService } from './types'; - -export type DataViewEditorServiceFactory = KibanaPluginServiceFactory< - DashboardDataViewEditorService, - DashboardStartDependencies ->; - -export const dataViewEditorServiceFactory: DataViewEditorServiceFactory = ({ startPlugins }) => { - const { - dataViewEditor: { openEditor, userPermissions }, - } = startPlugins; - - return { - openEditor, - userPermissions, - }; -}; diff --git a/src/plugins/dashboard/public/services/data_view_editor/types.ts b/src/plugins/dashboard/public/services/data_view_editor/types.ts deleted file mode 100644 index 3e5e7977f2119..0000000000000 --- a/src/plugins/dashboard/public/services/data_view_editor/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; -export interface DashboardDataViewEditorService { - openEditor: DataViewEditorStart['openEditor']; - userPermissions: DataViewEditorStart['userPermissions']; -} diff --git a/src/plugins/dashboard/public/services/documentation_links/documentation_links.stub.ts b/src/plugins/dashboard/public/services/documentation_links/documentation_links.stub.ts deleted file mode 100644 index e7d7a0f0c091d..0000000000000 --- a/src/plugins/dashboard/public/services/documentation_links/documentation_links.stub.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { coreMock } from '@kbn/core/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardDocumentationLinksService } from './types'; - -type DocumentationLinksServiceFactory = PluginServiceFactory; - -export const documentationLinksServiceFactory: DocumentationLinksServiceFactory = () => { - const corePluginMock = coreMock.createStart(); - - return { - indexPatternsDocLink: corePluginMock.docLinks.links.indexPatterns.introduction, - kibanaGuideDocLink: corePluginMock.docLinks.links.kibana.guide, - dashboardDocLink: corePluginMock.docLinks.links.dashboard.guide, - esqlDocLink: corePluginMock.docLinks.links.query.queryESQL, - }; -}; diff --git a/src/plugins/dashboard/public/services/documentation_links/documentation_links_service.ts b/src/plugins/dashboard/public/services/documentation_links/documentation_links_service.ts deleted file mode 100644 index 9dbe6a57aea0a..0000000000000 --- a/src/plugins/dashboard/public/services/documentation_links/documentation_links_service.ts +++ /dev/null @@ -1,39 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardDocumentationLinksService } from './types'; - -export type DocumentationLinksServiceFactory = KibanaPluginServiceFactory< - DashboardDocumentationLinksService, - DashboardStartDependencies ->; - -export const documentationLinksServiceFactory: DocumentationLinksServiceFactory = ({ - coreStart, -}) => { - const { - docLinks: { - links: { - kibana, - indexPatterns: { introduction }, - dashboard, - query: { queryESQL }, - }, - }, - } = coreStart; - - return { - indexPatternsDocLink: introduction, - kibanaGuideDocLink: kibana.guide, - dashboardDocLink: dashboard.guide, - esqlDocLink: queryESQL, - }; -}; diff --git a/src/plugins/dashboard/public/services/documentation_links/types.ts b/src/plugins/dashboard/public/services/documentation_links/types.ts deleted file mode 100644 index d79c95fc43aff..0000000000000 --- a/src/plugins/dashboard/public/services/documentation_links/types.ts +++ /dev/null @@ -1,17 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { CoreStart } from '@kbn/core/public'; - -export interface DashboardDocumentationLinksService { - indexPatternsDocLink: CoreStart['docLinks']['links']['indexPatterns']['introduction']; - kibanaGuideDocLink: CoreStart['docLinks']['links']['kibana']['guide']; - dashboardDocLink: CoreStart['docLinks']['links']['dashboard']['guide']; - esqlDocLink: CoreStart['docLinks']['links']['query']['queryESQL']; -} diff --git a/src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts b/src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts deleted file mode 100644 index 078f6b8cfe813..0000000000000 --- a/src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts +++ /dev/null @@ -1,29 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import type { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardEmbeddableService } from './types'; - -export type EmbeddableServiceFactory = PluginServiceFactory; - -export const embeddableServiceFactory: EmbeddableServiceFactory = () => { - const pluginMock = embeddablePluginMock.createStartContract(); - - return { - reactEmbeddableRegistryHasKey: pluginMock.reactEmbeddableRegistryHasKey, - getEmbeddableFactories: pluginMock.getEmbeddableFactories, - getEmbeddableFactory: pluginMock.getEmbeddableFactory, - getStateTransfer: pluginMock.getStateTransfer, - getAllMigrations: pluginMock.getAllMigrations, - telemetry: pluginMock.telemetry, - extract: pluginMock.extract, - inject: pluginMock.inject, - }; -}; diff --git a/src/plugins/dashboard/public/services/embeddable/embeddable_service.ts b/src/plugins/dashboard/public/services/embeddable/embeddable_service.ts deleted file mode 100644 index 6cce6426343a8..0000000000000 --- a/src/plugins/dashboard/public/services/embeddable/embeddable_service.ts +++ /dev/null @@ -1,34 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { pick } from 'lodash'; - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; - -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardEmbeddableService } from './types'; - -export type EmbeddableServiceFactory = KibanaPluginServiceFactory< - DashboardEmbeddableService, - DashboardStartDependencies ->; -export const embeddableServiceFactory: EmbeddableServiceFactory = ({ startPlugins }) => { - const { embeddable } = startPlugins; - - return pick(embeddable, [ - 'reactEmbeddableRegistryHasKey', - 'getEmbeddableFactory', - 'getEmbeddableFactories', - 'getStateTransfer', - 'getAllMigrations', - 'telemetry', - 'extract', - 'inject', - ]); -}; diff --git a/src/plugins/dashboard/public/services/embeddable/types.ts b/src/plugins/dashboard/public/services/embeddable/types.ts deleted file mode 100644 index 8b293a75fc823..0000000000000 --- a/src/plugins/dashboard/public/services/embeddable/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; - -export type DashboardEmbeddableService = Pick< - EmbeddableStart, - | 'reactEmbeddableRegistryHasKey' - | 'getEmbeddableFactories' - | 'getEmbeddableFactory' - | 'getAllMigrations' - | 'getStateTransfer' - | 'telemetry' - | 'extract' - | 'inject' ->; diff --git a/src/plugins/dashboard/public/services/http/http.stub.ts b/src/plugins/dashboard/public/services/http/http.stub.ts deleted file mode 100644 index f66ffa40e1098..0000000000000 --- a/src/plugins/dashboard/public/services/http/http.stub.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { coreMock } from '@kbn/core/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardHTTPService } from './types'; - -type HttpServiceFactory = PluginServiceFactory; - -export const httpServiceFactory: HttpServiceFactory = () => { - const serviceMock = coreMock.createStart(); - - return { - basePath: serviceMock.http.basePath, - get: serviceMock.http.get, - }; -}; diff --git a/src/plugins/dashboard/public/services/http/http_service.ts b/src/plugins/dashboard/public/services/http/http_service.ts deleted file mode 100644 index d06b7d718153f..0000000000000 --- a/src/plugins/dashboard/public/services/http/http_service.ts +++ /dev/null @@ -1,27 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardHTTPService } from './types'; - -export type HttpServiceFactory = KibanaPluginServiceFactory< - DashboardHTTPService, - DashboardStartDependencies ->; -export const httpServiceFactory: HttpServiceFactory = ({ coreStart }) => { - const { - http: { basePath, get }, - } = coreStart; - - return { - basePath, - get, - }; -}; diff --git a/src/plugins/dashboard/public/services/http/types.ts b/src/plugins/dashboard/public/services/http/types.ts deleted file mode 100644 index 532d9bb907f9d..0000000000000 --- a/src/plugins/dashboard/public/services/http/types.ts +++ /dev/null @@ -1,15 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardHTTPService { - basePath: CoreStart['http']['basePath']; - get: CoreStart['http']['get']; -} diff --git a/src/plugins/dashboard/public/services/i18n/i18n_service.ts b/src/plugins/dashboard/public/services/i18n/i18n_service.ts deleted file mode 100644 index e0ef2a3e36137..0000000000000 --- a/src/plugins/dashboard/public/services/i18n/i18n_service.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardI18nService } from './types'; - -export type I18nServiceFactory = KibanaPluginServiceFactory< - DashboardI18nService, - DashboardStartDependencies ->; -export const i18nServiceFactory: I18nServiceFactory = ({ coreStart }) => { - const { i18n } = coreStart; - - return i18n; -}; diff --git a/src/plugins/dashboard/public/services/i18n/types.ts b/src/plugins/dashboard/public/services/i18n/types.ts deleted file mode 100644 index 353ceab072034..0000000000000 --- a/src/plugins/dashboard/public/services/i18n/types.ts +++ /dev/null @@ -1,12 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export type DashboardI18nService = CoreStart['i18n']; diff --git a/src/plugins/dashboard/public/services/initializer_context/initializer_context.stub.ts b/src/plugins/dashboard/public/services/initializer_context/initializer_context.stub.ts deleted file mode 100644 index 9296d6cba94ee..0000000000000 --- a/src/plugins/dashboard/public/services/initializer_context/initializer_context.stub.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardInitializerContextService } from './types'; - -const defaultDashboardInitializerContext: DashboardInitializerContextService = { - kibanaVersion: 'test.kibana.version', - allowByValueEmbeddables: true, -}; - -type InitializerContextServiceFactory = PluginServiceFactory; - -export const initializerContextServiceFactory: InitializerContextServiceFactory = () => { - return { - ...defaultDashboardInitializerContext, - }; -}; diff --git a/src/plugins/dashboard/public/services/initializer_context/initializer_context_service.ts b/src/plugins/dashboard/public/services/initializer_context/initializer_context_service.ts deleted file mode 100644 index 90b4ad2f953e1..0000000000000 --- a/src/plugins/dashboard/public/services/initializer_context/initializer_context_service.ts +++ /dev/null @@ -1,32 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { DashboardFeatureFlagConfig } from '../..'; -import { DashboardPluginServiceParams } from '../types'; -import { DashboardInitializerContextService } from './types'; - -export type InitializerContextServiceFactory = ( - params: DashboardPluginServiceParams -) => DashboardInitializerContextService; - -export const initializerContextServiceFactory: InitializerContextServiceFactory = ({ - initContext, -}) => { - const { - env: { - packageInfo: { version }, - }, - config: { get }, - } = initContext; - - return { - kibanaVersion: version, - allowByValueEmbeddables: get().allowByValueEmbeddables, - }; -}; diff --git a/src/plugins/dashboard/public/services/initializer_context/types.ts b/src/plugins/dashboard/public/services/initializer_context/types.ts deleted file mode 100644 index cd8ea8cd41e0b..0000000000000 --- a/src/plugins/dashboard/public/services/initializer_context/types.ts +++ /dev/null @@ -1,13 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export interface DashboardInitializerContextService { - kibanaVersion: string; - allowByValueEmbeddables: boolean; -} diff --git a/src/plugins/dashboard/public/services/kibana_services.ts b/src/plugins/dashboard/public/services/kibana_services.ts new file mode 100644 index 0000000000000..e8b164d47b413 --- /dev/null +++ b/src/plugins/dashboard/public/services/kibana_services.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { BehaviorSubject } from 'rxjs'; + +import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import type { CoreStart } from '@kbn/core/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; +import type { EmbeddableStart } from '@kbn/embeddable-plugin/public/plugin'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public/plugin'; +import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; +import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; +import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import type { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; +import type { ServerlessPluginStart } from '@kbn/serverless/public'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; +import type { SpacesApi } from '@kbn/spaces-plugin/public'; +import type { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin'; +import type { UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; +import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import type { VisualizationsStart } from '@kbn/visualizations-plugin/public'; + +import type { DashboardStartDependencies } from '../plugin'; + +export let coreServices: CoreStart; +export let contentManagementService: ContentManagementPublicStart; +export let dataService: DataPublicPluginStart; +export let dataViewEditorService: DataViewEditorStart; +export let embeddableService: EmbeddableStart; +export let fieldFormatService: FieldFormatsStart; +export let navigationService: NavigationPublicPluginStart; +export let noDataPageService: NoDataPagePluginStart | undefined; +export let observabilityAssistantService: ObservabilityAIAssistantPublicStart | undefined; +export let presentationUtilService: PresentationUtilPluginStart; +export let savedObjectsTaggingService: SavedObjectTaggingOssPluginStart | undefined; +export let screenshotModeService: ScreenshotModePluginStart; +export let serverlessService: ServerlessPluginStart | undefined; +export let shareService: SharePluginStart | undefined; +export let spacesService: SpacesApi | undefined; +export let uiActionsService: UiActionsPublicStart; +export let urlForwardingService: UrlForwardingStart; +export let usageCollectionService: UsageCollectionStart | undefined; +export let visualizationsService: VisualizationsStart; + +const servicesReady$ = new BehaviorSubject(false); + +export const setKibanaServices = (kibanaCore: CoreStart, deps: DashboardStartDependencies) => { + coreServices = kibanaCore; + contentManagementService = deps.contentManagement; + dataService = deps.data; + dataViewEditorService = deps.dataViewEditor; + embeddableService = deps.embeddable; + fieldFormatService = deps.fieldFormats; + navigationService = deps.navigation; + noDataPageService = deps.noDataPage; + observabilityAssistantService = deps.observabilityAIAssistant; + presentationUtilService = deps.presentationUtil; + savedObjectsTaggingService = deps.savedObjectsTaggingOss; + serverlessService = deps.serverless; + screenshotModeService = deps.screenshotMode; + shareService = deps.share; + spacesService = deps.spaces; + uiActionsService = deps.uiActions; + urlForwardingService = deps.urlForwarding; + usageCollectionService = deps.usageCollection; + visualizationsService = deps.visualizations; + + servicesReady$.next(true); +}; + +export const untilPluginStartServicesReady = () => { + if (servicesReady$.value) return Promise.resolve(); + return new Promise((resolve) => { + const subscription = servicesReady$.subscribe((isInitialized) => { + if (isInitialized) { + subscription.unsubscribe(); + resolve(); + } + }); + }); +}; diff --git a/src/plugins/dashboard/public/services/mocks.ts b/src/plugins/dashboard/public/services/mocks.ts index a4126962432c6..61132c2fc264e 100644 --- a/src/plugins/dashboard/public/services/mocks.ts +++ b/src/plugins/dashboard/public/services/mocks.ts @@ -7,11 +7,148 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { pluginServices } from './plugin_services'; -import { registry } from './plugin_services.stub'; +import { serverlessMock } from '@kbn/serverless/public/mocks'; +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; +import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks'; +import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; +import { coreMock } from '@kbn/core/public/mocks'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; +import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; +import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; +import { indexPatternEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks'; +import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks'; +import { navigationPluginMock } from '@kbn/navigation-plugin/public/mocks'; +import { noDataPagePublicMock } from '@kbn/no-data-page-plugin/public/mocks'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; +import { presentationUtilPluginMock } from '@kbn/presentation-util-plugin/public/mocks'; +import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks'; +import { screenshotModePluginMock } from '@kbn/screenshot-mode-plugin/public/mocks'; +import { savedObjectTaggingOssPluginMock } from '@kbn/saved-objects-tagging-oss-plugin/public/mocks'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { urlForwardingPluginMock } from '@kbn/url-forwarding-plugin/public/mocks'; +import { visualizationsPluginMock } from '@kbn/visualizations-plugin/public/mocks'; -export function setStubDashboardServices() { - pluginServices.setRegistry(registry.start({})); -} +import { setKibanaServices } from './kibana_services'; +import { DashboardAttributes, DashboardCapabilities } from '../../common'; +import { LoadDashboardReturn } from './dashboard_content_management_service/types'; +import { SearchDashboardsResponse } from './dashboard_content_management_service/lib/find_dashboards'; -setStubDashboardServices(); +const defaultDashboardCapabilities: DashboardCapabilities = { + show: true, + createNew: true, + saveQuery: true, + createShortUrl: true, + showWriteControls: true, + storeSearchSession: true, +}; + +export const setStubKibanaServices = () => { + const core = coreMock.createStart(); + (core.application.capabilities as any).dashboard = defaultDashboardCapabilities; + + setKibanaServices(core, { + contentManagement: contentManagementMock.createStartContract(), + customBranding: customBrandingServiceMock.createStartContract(), + data: dataPluginMock.createStartContract(), + dataViewEditor: indexPatternEditorPluginMock.createStartContract(), + embeddable: embeddablePluginMock.createStartContract(), + fieldFormats: fieldFormatsServiceMock.createStartContract(), + inspector: inspectorPluginMock.createStartContract(), + navigation: navigationPluginMock.createStartContract(), + noDataPage: noDataPagePublicMock.createStart(), + observabilityAIAssistant: observabilityAIAssistantPluginMock.createStartContract(), + presentationUtil: presentationUtilPluginMock.createStartContract(core), + savedObjectsManagement: savedObjectsManagementPluginMock.createStartContract(), + savedObjectsTaggingOss: savedObjectTaggingOssPluginMock.createStart(), + screenshotMode: screenshotModePluginMock.createStartContract(), + serverless: serverlessMock.createStart(), + share: sharePluginMock.createStartContract(), + spaces: spacesPluginMock.createStartContract(), + uiActions: uiActionsPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), + urlForwarding: urlForwardingPluginMock.createStartContract(), + usageCollection: usageCollectionPluginMock.createSetupContract(), + visualizations: visualizationsPluginMock.createStartContract(), + }); +}; + +export const mockDashboardContentManagementService = { + loadDashboardState: jest.fn().mockImplementation(() => + Promise.resolve({ + dashboardInput: {}, + } as LoadDashboardReturn) + ), + saveDashboardState: jest.fn(), + findDashboards: { + search: jest.fn().mockImplementation(({ search, size }) => { + const sizeToUse = size ?? 10; + const hits: SearchDashboardsResponse['hits'] = []; + for (let i = 0; i < sizeToUse; i++) { + hits.push({ + type: 'dashboard', + id: `dashboard${i}`, + attributes: { + description: `dashboard${i} desc`, + title: `dashboard${i} - ${search} - title`, + }, + references: [] as SearchDashboardsResponse['hits'][0]['references'], + } as SearchDashboardsResponse['hits'][0]); + } + return Promise.resolve({ + total: sizeToUse, + hits, + }); + }), + findById: jest.fn(), + findByIds: jest.fn().mockImplementation(() => + Promise.resolve([ + { + id: `dashboardUnsavedOne`, + status: 'success', + attributes: { + title: `Dashboard Unsaved One`, + } as unknown as DashboardAttributes, + }, + { + id: `dashboardUnsavedTwo`, + status: 'success', + attributes: { + title: `Dashboard Unsaved Two`, + } as unknown as DashboardAttributes, + }, + { + id: `dashboardUnsavedThree`, + status: 'success', + attributes: { + title: `Dashboard Unsaved Three`, + } as unknown as DashboardAttributes, + }, + ]) + ), + findByTitle: jest.fn(), + }, + deleteDashboards: jest.fn(), + checkForDuplicateDashboardTitle: jest.fn(), + updateDashboardMeta: jest.fn(), +}; + +export const mockDashboardBackupService = { + clearState: jest.fn(), + getState: jest.fn().mockReturnValue(undefined), + setState: jest.fn(), + getViewMode: jest.fn(), + storeViewMode: jest.fn(), + getDashboardIdsWithUnsavedChanges: jest + .fn() + .mockReturnValue(['dashboardUnsavedOne', 'dashboardUnsavedTwo']), + dashboardHasUnsavedEdits: jest.fn(), +}; + +export const mockDashboardContentManagementCache = { + fetchDashboard: jest.fn(), + addDashboard: jest.fn(), + deleteDashboard: jest.fn(), +}; diff --git a/src/plugins/dashboard/public/services/navigation/navigation.stub.ts b/src/plugins/dashboard/public/services/navigation/navigation.stub.ts deleted file mode 100644 index 81b5c0734574d..0000000000000 --- a/src/plugins/dashboard/public/services/navigation/navigation.stub.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { navigationPluginMock } from '@kbn/navigation-plugin/public/mocks'; -import { DashboardNavigationService } from './types'; - -type NavigationServiceFactory = PluginServiceFactory; - -export const navigationServiceFactory: NavigationServiceFactory = () => { - const pluginMock = navigationPluginMock.createStartContract(); - - return { - TopNavMenu: pluginMock.ui.TopNavMenu, - }; -}; diff --git a/src/plugins/dashboard/public/services/navigation/navigation_service.ts b/src/plugins/dashboard/public/services/navigation/navigation_service.ts deleted file mode 100644 index 7bb3f97220fc3..0000000000000 --- a/src/plugins/dashboard/public/services/navigation/navigation_service.ts +++ /dev/null @@ -1,28 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardNavigationService } from './types'; - -export type NavigationServiceFactory = KibanaPluginServiceFactory< - DashboardNavigationService, - DashboardStartDependencies ->; -export const navigationServiceFactory: NavigationServiceFactory = ({ startPlugins }) => { - const { - navigation: { - ui: { TopNavMenu }, - }, - } = startPlugins; - - return { - TopNavMenu, - }; -}; diff --git a/src/plugins/dashboard/public/services/navigation/types.ts b/src/plugins/dashboard/public/services/navigation/types.ts deleted file mode 100644 index e407e8e77b769..0000000000000 --- a/src/plugins/dashboard/public/services/navigation/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; - -export interface DashboardNavigationService { - TopNavMenu: NavigationPublicPluginStart['ui']['TopNavMenu']; -} diff --git a/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.stub.ts b/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.stub.ts deleted file mode 100644 index 6797d5ef9e7d8..0000000000000 --- a/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.stub.ts +++ /dev/null @@ -1,17 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { noDataPagePublicMock } from '@kbn/no-data-page-plugin/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { NoDataPageService } from './types'; - -export type NoDataPageServiceFactory = PluginServiceFactory; - -export const noDataPageServiceFactory: NoDataPageServiceFactory = () => - noDataPagePublicMock.createSetup(); diff --git a/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.ts b/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.ts deleted file mode 100644 index 13000ca2cdddb..0000000000000 --- a/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardStartDependencies } from '../../plugin'; -import { NoDataPageService } from './types'; - -export type NoDataPageServiceFactory = KibanaPluginServiceFactory< - NoDataPageService, - DashboardStartDependencies ->; - -export const noDataPageServiceFactory: NoDataPageServiceFactory = ({ startPlugins }) => { - const { noDataPage } = startPlugins; - - return { - getAnalyticsNoDataPageFlavor: noDataPage?.getAnalyticsNoDataPageFlavor ?? (() => 'kibana'), - }; -}; diff --git a/src/plugins/dashboard/public/services/no_data_page/types.ts b/src/plugins/dashboard/public/services/no_data_page/types.ts deleted file mode 100644 index 81700402db72d..0000000000000 --- a/src/plugins/dashboard/public/services/no_data_page/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; - -export interface NoDataPageService { - getAnalyticsNoDataPageFlavor: NoDataPagePluginStart['getAnalyticsNoDataPageFlavor']; -} diff --git a/src/plugins/dashboard/public/services/notifications/notifications.stub.ts b/src/plugins/dashboard/public/services/notifications/notifications.stub.ts deleted file mode 100644 index 0be8056dd604b..0000000000000 --- a/src/plugins/dashboard/public/services/notifications/notifications.stub.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardNotificationsService } from './types'; - -type NotificationsServiceFactory = PluginServiceFactory; - -export const notificationsServiceFactory: NotificationsServiceFactory = () => { - const pluginMock = notificationServiceMock.createStartContract(); - - return { - toasts: pluginMock.toasts, - showErrorDialog: pluginMock.showErrorDialog, - }; -}; diff --git a/src/plugins/dashboard/public/services/notifications/notifications_service.ts b/src/plugins/dashboard/public/services/notifications/notifications_service.ts deleted file mode 100644 index c2acd02891492..0000000000000 --- a/src/plugins/dashboard/public/services/notifications/notifications_service.ts +++ /dev/null @@ -1,28 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardNotificationsService } from './types'; - -export type NotificationsServiceFactory = KibanaPluginServiceFactory< - DashboardNotificationsService, - DashboardStartDependencies ->; - -export const notificationsServiceFactory: NotificationsServiceFactory = ({ coreStart }) => { - const { - notifications: { toasts, showErrorDialog }, - } = coreStart; - - return { - toasts, - showErrorDialog, - }; -}; diff --git a/src/plugins/dashboard/public/services/notifications/types.ts b/src/plugins/dashboard/public/services/notifications/types.ts deleted file mode 100644 index f31420238f175..0000000000000 --- a/src/plugins/dashboard/public/services/notifications/types.ts +++ /dev/null @@ -1,15 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardNotificationsService { - toasts: CoreStart['notifications']['toasts']; - showErrorDialog: CoreStart['notifications']['showErrorDialog']; -} diff --git a/src/plugins/dashboard/public/services/observability_ai_assistant/observability_ai_assistant_service.stub.ts b/src/plugins/dashboard/public/services/observability_ai_assistant/observability_ai_assistant_service.stub.ts deleted file mode 100644 index d33491f0024f3..0000000000000 --- a/src/plugins/dashboard/public/services/observability_ai_assistant/observability_ai_assistant_service.stub.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; -import { ObservabilityAIAssistantService } from './types'; - -type ObservabilityAIAssistantServiceFactory = PluginServiceFactory; - -export const observabilityAIAssistantServiceStubFactory: ObservabilityAIAssistantServiceFactory = - () => { - const pluginMock = observabilityAIAssistantPluginMock.createStartContract(); - return { - start: pluginMock, - }; - }; diff --git a/src/plugins/dashboard/public/services/observability_ai_assistant/observability_ai_assistant_service.ts b/src/plugins/dashboard/public/services/observability_ai_assistant/observability_ai_assistant_service.ts deleted file mode 100644 index 5b3b08f148f1a..0000000000000 --- a/src/plugins/dashboard/public/services/observability_ai_assistant/observability_ai_assistant_service.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { ObservabilityAIAssistantService } from './types'; - -export type ObservabilityAIAssistantServiceFactory = KibanaPluginServiceFactory< - ObservabilityAIAssistantService, - DashboardStartDependencies ->; -export const observabilityAIAssistantServiceFactory: ObservabilityAIAssistantServiceFactory = ({ - startPlugins, -}) => { - return startPlugins.observabilityAIAssistant - ? { start: startPlugins.observabilityAIAssistant } - : {}; -}; diff --git a/src/plugins/dashboard/public/services/observability_ai_assistant/types.ts b/src/plugins/dashboard/public/services/observability_ai_assistant/types.ts deleted file mode 100644 index 6cb252910219a..0000000000000 --- a/src/plugins/dashboard/public/services/observability_ai_assistant/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public'; - -export interface ObservabilityAIAssistantService { - start?: ObservabilityAIAssistantPublicStart; -} diff --git a/src/plugins/dashboard/public/services/overlays/overlays.stub.ts b/src/plugins/dashboard/public/services/overlays/overlays.stub.ts deleted file mode 100644 index 0fc7d90fa13fd..0000000000000 --- a/src/plugins/dashboard/public/services/overlays/overlays.stub.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardOverlaysService } from './types'; - -type OverlaysServiceFactory = PluginServiceFactory; - -export const overlaysServiceFactory: OverlaysServiceFactory = () => { - const pluginMock = overlayServiceMock.createStartContract(); - - return { - banners: pluginMock.banners, - openConfirm: pluginMock.openConfirm, - openFlyout: pluginMock.openFlyout, - openModal: pluginMock.openModal, - }; -}; diff --git a/src/plugins/dashboard/public/services/overlays/overlays_service.ts b/src/plugins/dashboard/public/services/overlays/overlays_service.ts deleted file mode 100644 index aca127b116697..0000000000000 --- a/src/plugins/dashboard/public/services/overlays/overlays_service.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardOverlaysService } from './types'; - -export type OverlaysServiceFactory = KibanaPluginServiceFactory< - DashboardOverlaysService, - DashboardStartDependencies ->; - -export const overlaysServiceFactory: OverlaysServiceFactory = ({ coreStart }) => { - const { - overlays: { banners, openConfirm, openFlyout, openModal }, - } = coreStart; - - return { - banners, - openConfirm, - openFlyout, - openModal, - }; -}; diff --git a/src/plugins/dashboard/public/services/overlays/types.ts b/src/plugins/dashboard/public/services/overlays/types.ts deleted file mode 100644 index f2a2dd8feb509..0000000000000 --- a/src/plugins/dashboard/public/services/overlays/types.ts +++ /dev/null @@ -1,17 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardOverlaysService { - banners: CoreStart['overlays']['banners']; - openConfirm: CoreStart['overlays']['openConfirm']; - openFlyout: CoreStart['overlays']['openFlyout']; - openModal: CoreStart['overlays']['openModal']; -} diff --git a/src/plugins/dashboard/public/services/plugin_services.stub.ts b/src/plugins/dashboard/public/services/plugin_services.stub.ts deleted file mode 100644 index a9334c001c2c0..0000000000000 --- a/src/plugins/dashboard/public/services/plugin_services.stub.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { - PluginServiceProviders, - PluginServiceProvider, - PluginServiceRegistry, -} from '@kbn/presentation-util-plugin/public'; - -import { DashboardServices } from './types'; - -import { analyticsServiceFactory } from './analytics/analytics.stub'; -import { applicationServiceFactory } from './application/application.stub'; -import { chromeServiceFactory } from './chrome/chrome.stub'; -import { coreContextServiceFactory } from './core_context/core_context.stub'; -import { dashboardCapabilitiesServiceFactory } from './dashboard_capabilities/dashboard_capabilities.stub'; -import { dashboardBackupServiceFactory } from './dashboard_backup/dashboard_backup.stub'; -import { dataServiceFactory } from './data/data.stub'; -import { dataViewEditorServiceFactory } from './data_view_editor/data_view_editor.stub'; -import { documentationLinksServiceFactory } from './documentation_links/documentation_links.stub'; -import { embeddableServiceFactory } from './embeddable/embeddable.stub'; -import { httpServiceFactory } from './http/http.stub'; -import { i18nServiceFactory } from './i18n/i18n.stub'; -import { initializerContextServiceFactory } from './initializer_context/initializer_context.stub'; -import { navigationServiceFactory } from './navigation/navigation.stub'; -import { notificationsServiceFactory } from './notifications/notifications.stub'; -import { overlaysServiceFactory } from './overlays/overlays.stub'; -import { savedObjectsTaggingServiceFactory } from './saved_objects_tagging/saved_objects_tagging.stub'; -import { screenshotModeServiceFactory } from './screenshot_mode/screenshot_mode.stub'; -import { settingsServiceFactory } from './settings/settings.stub'; -import { shareServiceFactory } from './share/share.stub'; -import { usageCollectionServiceFactory } from './usage_collection/usage_collection.stub'; -import { spacesServiceFactory } from './spaces/spaces.stub'; -import { urlForwardingServiceFactory } from './url_forwarding/url_fowarding.stub'; -import { visualizationsServiceFactory } from './visualizations/visualizations.stub'; -import { dashboardContentManagementServiceFactory } from './dashboard_content_management/dashboard_content_management.stub'; -import { customBrandingServiceFactory } from './custom_branding/custom_branding.stub'; -import { savedObjectsManagementServiceFactory } from './saved_objects_management/saved_objects_management_service.stub'; -import { contentManagementServiceFactory } from './content_management/content_management_service.stub'; -import { serverlessServiceFactory } from './serverless/serverless_service.stub'; -import { userProfileServiceFactory } from './user_profile/user_profile_service.stub'; -import { observabilityAIAssistantServiceStubFactory } from './observability_ai_assistant/observability_ai_assistant_service.stub'; -import { noDataPageServiceFactory } from './no_data_page/no_data_page_service.stub'; -import { uiActionsServiceFactory } from './ui_actions/ui_actions_service.stub'; -import { dashboardRecentlyAccessedServiceFactory } from './dashboard_recently_accessed/dashboard_recently_accessed.stub'; -import { dashboardFavoritesServiceFactory } from './dashboard_favorites/dashboard_favorites_service.stub'; -import { dashboardContentInsightsServiceFactory } from './dashboard_content_insights/dashboard_content_insights.stub'; - -export const providers: PluginServiceProviders = { - dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory), - analytics: new PluginServiceProvider(analyticsServiceFactory), - application: new PluginServiceProvider(applicationServiceFactory), - chrome: new PluginServiceProvider(chromeServiceFactory), - coreContext: new PluginServiceProvider(coreContextServiceFactory), - dashboardCapabilities: new PluginServiceProvider(dashboardCapabilitiesServiceFactory), - dashboardBackup: new PluginServiceProvider(dashboardBackupServiceFactory), - data: new PluginServiceProvider(dataServiceFactory), - dataViewEditor: new PluginServiceProvider(dataViewEditorServiceFactory), - documentationLinks: new PluginServiceProvider(documentationLinksServiceFactory), - embeddable: new PluginServiceProvider(embeddableServiceFactory), - http: new PluginServiceProvider(httpServiceFactory), - i18n: new PluginServiceProvider(i18nServiceFactory), - initializerContext: new PluginServiceProvider(initializerContextServiceFactory), - navigation: new PluginServiceProvider(navigationServiceFactory), - notifications: new PluginServiceProvider(notificationsServiceFactory), - overlays: new PluginServiceProvider(overlaysServiceFactory), - savedObjectsTagging: new PluginServiceProvider(savedObjectsTaggingServiceFactory), - screenshotMode: new PluginServiceProvider(screenshotModeServiceFactory), - settings: new PluginServiceProvider(settingsServiceFactory), - share: new PluginServiceProvider(shareServiceFactory), - spaces: new PluginServiceProvider(spacesServiceFactory), - urlForwarding: new PluginServiceProvider(urlForwardingServiceFactory), - usageCollection: new PluginServiceProvider(usageCollectionServiceFactory), - visualizations: new PluginServiceProvider(visualizationsServiceFactory), - customBranding: new PluginServiceProvider(customBrandingServiceFactory), - savedObjectsManagement: new PluginServiceProvider(savedObjectsManagementServiceFactory), - contentManagement: new PluginServiceProvider(contentManagementServiceFactory), - serverless: new PluginServiceProvider(serverlessServiceFactory), - noDataPage: new PluginServiceProvider(noDataPageServiceFactory), - uiActions: new PluginServiceProvider(uiActionsServiceFactory), - userProfile: new PluginServiceProvider(userProfileServiceFactory), - observabilityAIAssistant: new PluginServiceProvider(observabilityAIAssistantServiceStubFactory), - dashboardRecentlyAccessed: new PluginServiceProvider(dashboardRecentlyAccessedServiceFactory), - dashboardContentInsights: new PluginServiceProvider(dashboardContentInsightsServiceFactory), - dashboardFavorites: new PluginServiceProvider(dashboardFavoritesServiceFactory), -}; - -export const registry = new PluginServiceRegistry(providers); diff --git a/src/plugins/dashboard/public/services/plugin_services.ts b/src/plugins/dashboard/public/services/plugin_services.ts deleted file mode 100644 index a3b826bfe5632..0000000000000 --- a/src/plugins/dashboard/public/services/plugin_services.ts +++ /dev/null @@ -1,112 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { - PluginServiceProviders, - PluginServiceProvider, - PluginServiceRegistry, - PluginServices, -} from '@kbn/presentation-util-plugin/public'; - -import { DashboardPluginServiceParams, DashboardServices } from './types'; - -import { applicationServiceFactory } from './application/application_service'; -import { chromeServiceFactory } from './chrome/chrome_service'; -import { coreContextServiceFactory } from './core_context/core_context_service'; -import { dashboardCapabilitiesServiceFactory } from './dashboard_capabilities/dashboard_capabilities_service'; -import { dashboardBackupServiceFactory } from './dashboard_backup/dashboard_backup_service'; -import { dataServiceFactory } from './data/data_service'; -import { dataViewEditorServiceFactory } from './data_view_editor/data_view_editor_service'; -import { documentationLinksServiceFactory } from './documentation_links/documentation_links_service'; -import { embeddableServiceFactory } from './embeddable/embeddable_service'; -import { httpServiceFactory } from './http/http_service'; -import { i18nServiceFactory } from './i18n/i18n_service'; -import { initializerContextServiceFactory } from './initializer_context/initializer_context_service'; -import { navigationServiceFactory } from './navigation/navigation_service'; -import { notificationsServiceFactory } from './notifications/notifications_service'; -import { overlaysServiceFactory } from './overlays/overlays_service'; -import { screenshotModeServiceFactory } from './screenshot_mode/screenshot_mode_service'; -import { savedObjectsTaggingServiceFactory } from './saved_objects_tagging/saved_objects_tagging_service'; -import { settingsServiceFactory } from './settings/settings_service'; -import { shareServiceFactory } from './share/share_services'; -import { spacesServiceFactory } from './spaces/spaces_service'; -import { urlForwardingServiceFactory } from './url_forwarding/url_forwarding_service'; -import { visualizationsServiceFactory } from './visualizations/visualizations_service'; -import { usageCollectionServiceFactory } from './usage_collection/usage_collection_service'; -import { analyticsServiceFactory } from './analytics/analytics_service'; -import { customBrandingServiceFactory } from './custom_branding/custom_branding_service'; -import { savedObjectsManagementServiceFactory } from './saved_objects_management/saved_objects_management_service'; -import { dashboardContentManagementServiceFactory } from './dashboard_content_management/dashboard_content_management_service'; -import { contentManagementServiceFactory } from './content_management/content_management_service'; -import { serverlessServiceFactory } from './serverless/serverless_service'; -import { noDataPageServiceFactory } from './no_data_page/no_data_page_service'; -import { uiActionsServiceFactory } from './ui_actions/ui_actions_service'; -import { observabilityAIAssistantServiceFactory } from './observability_ai_assistant/observability_ai_assistant_service'; -import { userProfileServiceFactory } from './user_profile/user_profile_service'; -import { dashboardRecentlyAccessedFactory } from './dashboard_recently_accessed/dashboard_recently_accessed'; -import { dashboardFavoritesServiceFactory } from './dashboard_favorites/dashboard_favorites_service'; -import { dashboardContentInsightsServiceFactory } from './dashboard_content_insights/dashboard_content_insights_service'; - -const providers: PluginServiceProviders = { - dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory, [ - 'savedObjectsTagging', - 'initializerContext', - 'dashboardBackup', - 'screenshotMode', - 'notifications', - 'embeddable', - 'spaces', - 'data', - ]), - dashboardBackup: new PluginServiceProvider(dashboardBackupServiceFactory, [ - 'notifications', - 'spaces', - ]), - - analytics: new PluginServiceProvider(analyticsServiceFactory), - application: new PluginServiceProvider(applicationServiceFactory), - chrome: new PluginServiceProvider(chromeServiceFactory), - coreContext: new PluginServiceProvider(coreContextServiceFactory), - dashboardCapabilities: new PluginServiceProvider(dashboardCapabilitiesServiceFactory), - data: new PluginServiceProvider(dataServiceFactory), - dataViewEditor: new PluginServiceProvider(dataViewEditorServiceFactory), - documentationLinks: new PluginServiceProvider(documentationLinksServiceFactory), - embeddable: new PluginServiceProvider(embeddableServiceFactory), - http: new PluginServiceProvider(httpServiceFactory), - i18n: new PluginServiceProvider(i18nServiceFactory), - initializerContext: new PluginServiceProvider(initializerContextServiceFactory), - navigation: new PluginServiceProvider(navigationServiceFactory), - notifications: new PluginServiceProvider(notificationsServiceFactory), - overlays: new PluginServiceProvider(overlaysServiceFactory), - savedObjectsTagging: new PluginServiceProvider(savedObjectsTaggingServiceFactory), - screenshotMode: new PluginServiceProvider(screenshotModeServiceFactory), - settings: new PluginServiceProvider(settingsServiceFactory), - share: new PluginServiceProvider(shareServiceFactory), - spaces: new PluginServiceProvider(spacesServiceFactory), - urlForwarding: new PluginServiceProvider(urlForwardingServiceFactory), - usageCollection: new PluginServiceProvider(usageCollectionServiceFactory), - visualizations: new PluginServiceProvider(visualizationsServiceFactory), - customBranding: new PluginServiceProvider(customBrandingServiceFactory), - savedObjectsManagement: new PluginServiceProvider(savedObjectsManagementServiceFactory), - contentManagement: new PluginServiceProvider(contentManagementServiceFactory), - serverless: new PluginServiceProvider(serverlessServiceFactory), - noDataPage: new PluginServiceProvider(noDataPageServiceFactory), - uiActions: new PluginServiceProvider(uiActionsServiceFactory), - observabilityAIAssistant: new PluginServiceProvider(observabilityAIAssistantServiceFactory), - userProfile: new PluginServiceProvider(userProfileServiceFactory), - dashboardRecentlyAccessed: new PluginServiceProvider(dashboardRecentlyAccessedFactory, ['http']), - dashboardContentInsights: new PluginServiceProvider(dashboardContentInsightsServiceFactory), - dashboardFavorites: new PluginServiceProvider(dashboardFavoritesServiceFactory), -}; - -export const pluginServices = new PluginServices(); - -export const registry = new PluginServiceRegistry( - providers -); diff --git a/src/plugins/dashboard/public/services/saved_objects_management/saved_objects_management_service.stub.ts b/src/plugins/dashboard/public/services/saved_objects_management/saved_objects_management_service.stub.ts deleted file mode 100644 index 085387b25eaf7..0000000000000 --- a/src/plugins/dashboard/public/services/saved_objects_management/saved_objects_management_service.stub.ts +++ /dev/null @@ -1,18 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; -import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks'; - -type SavedObjectsManagementServiceFactory = PluginServiceFactory; - -export const savedObjectsManagementServiceFactory: SavedObjectsManagementServiceFactory = () => { - return savedObjectsManagementPluginMock.createStartContract(); -}; diff --git a/src/plugins/dashboard/public/services/saved_objects_management/saved_objects_management_service.ts b/src/plugins/dashboard/public/services/saved_objects_management/saved_objects_management_service.ts deleted file mode 100644 index 3f471b0f12d47..0000000000000 --- a/src/plugins/dashboard/public/services/saved_objects_management/saved_objects_management_service.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; -import { DashboardStartDependencies } from '../../plugin'; - -export type SavedObjectsManagementServiceFactory = KibanaPluginServiceFactory< - SavedObjectsManagementPluginStart, - DashboardStartDependencies ->; - -export const savedObjectsManagementServiceFactory: SavedObjectsManagementServiceFactory = ({ - startPlugins, -}) => { - const { savedObjectsManagement } = startPlugins; - - return savedObjectsManagement; -}; diff --git a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging.stub.ts b/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging.stub.ts deleted file mode 100644 index ed8e4e5486dad..0000000000000 --- a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging.stub.ts +++ /dev/null @@ -1,29 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/mocks'; -import { DashboardSavedObjectsTaggingService } from './types'; - -type SavedObjectsTaggingServiceFactory = PluginServiceFactory; - -export const savedObjectsTaggingServiceFactory: SavedObjectsTaggingServiceFactory = () => { - const pluginMock = taggingApiMock.createUi(); - - return { - hasApi: true, - - // I'm not defining components so that I don't have to update the snapshot of `save_modal.test` - // However, if it's ever necessary, it can be done via: `components: pluginMock.components`, - parseSearchQuery: pluginMock.parseSearchQuery, - getSearchBarFilter: pluginMock.getSearchBarFilter, - getTagIdsFromReferences: pluginMock.getTagIdsFromReferences, - getTableColumnDefinition: pluginMock.getTableColumnDefinition, - }; -}; diff --git a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging_service.ts b/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging_service.ts deleted file mode 100644 index 4440ee069d2bc..0000000000000 --- a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging_service.ts +++ /dev/null @@ -1,51 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardSavedObjectsTaggingService } from './types'; - -export type SavedObjectsTaggingServiceFactory = KibanaPluginServiceFactory< - DashboardSavedObjectsTaggingService, - DashboardStartDependencies ->; -export const savedObjectsTaggingServiceFactory: SavedObjectsTaggingServiceFactory = ({ - startPlugins, -}) => { - const { savedObjectsTaggingOss } = startPlugins; - if (!savedObjectsTaggingOss) return { hasApi: false }; - - const { getTaggingApi } = savedObjectsTaggingOss; - const taggingApi = getTaggingApi(); - if (!taggingApi) return { hasApi: false }; - - const { - ui: { - components, - parseSearchQuery, - getSearchBarFilter, - updateTagsReferences, - getTagIdsFromReferences, - getTableColumnDefinition, - getTagList, - }, - } = taggingApi; - - return { - hasApi: true, - api: taggingApi, - components, - parseSearchQuery, - getSearchBarFilter, - updateTagsReferences, - getTagIdsFromReferences, - getTableColumnDefinition, - getTagList, - }; -}; diff --git a/src/plugins/dashboard/public/services/saved_objects_tagging/types.ts b/src/plugins/dashboard/public/services/saved_objects_tagging/types.ts deleted file mode 100644 index f08aa7c6c28f2..0000000000000 --- a/src/plugins/dashboard/public/services/saved_objects_tagging/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; - -export interface DashboardSavedObjectsTaggingService { - hasApi: boolean; // remove this once the entire service is optional - api?: SavedObjectsTaggingApi; - components?: SavedObjectsTaggingApi['ui']['components']; - parseSearchQuery?: SavedObjectsTaggingApi['ui']['parseSearchQuery']; - getSearchBarFilter?: SavedObjectsTaggingApi['ui']['getSearchBarFilter']; - updateTagsReferences?: SavedObjectsTaggingApi['ui']['updateTagsReferences']; - getTagIdsFromReferences?: SavedObjectsTaggingApi['ui']['getTagIdsFromReferences']; - getTableColumnDefinition?: SavedObjectsTaggingApi['ui']['getTableColumnDefinition']; - getTagList?: SavedObjectsTaggingApi['ui']['getTagList']; -} diff --git a/src/plugins/dashboard/public/services/screenshot_mode/screenshot_mode.stub.ts b/src/plugins/dashboard/public/services/screenshot_mode/screenshot_mode.stub.ts deleted file mode 100644 index ef7c6760b03f8..0000000000000 --- a/src/plugins/dashboard/public/services/screenshot_mode/screenshot_mode.stub.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { screenshotModePluginMock } from '@kbn/screenshot-mode-plugin/public/mocks'; -import { DashboardScreenshotModeService } from './types'; - -type ScreenshotModeServiceFactory = PluginServiceFactory; - -export const screenshotModeServiceFactory: ScreenshotModeServiceFactory = () => { - const pluginMock = screenshotModePluginMock.createStartContract(); - - return { - isScreenshotMode: pluginMock.isScreenshotMode, - getScreenshotContext: pluginMock.getScreenshotContext, - }; -}; diff --git a/src/plugins/dashboard/public/services/screenshot_mode/screenshot_mode_service.ts b/src/plugins/dashboard/public/services/screenshot_mode/screenshot_mode_service.ts deleted file mode 100644 index 2e7e160ce93fc..0000000000000 --- a/src/plugins/dashboard/public/services/screenshot_mode/screenshot_mode_service.ts +++ /dev/null @@ -1,27 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardScreenshotModeService } from './types'; - -export type ScreenshotModeServiceFactory = KibanaPluginServiceFactory< - DashboardScreenshotModeService, - DashboardStartDependencies ->; -export const screenshotModeServiceFactory: ScreenshotModeServiceFactory = ({ startPlugins }) => { - const { - screenshotMode: { isScreenshotMode, getScreenshotContext }, - } = startPlugins; - - return { - isScreenshotMode, - getScreenshotContext, - }; -}; diff --git a/src/plugins/dashboard/public/services/screenshot_mode/types.ts b/src/plugins/dashboard/public/services/screenshot_mode/types.ts deleted file mode 100644 index 464ca9134960a..0000000000000 --- a/src/plugins/dashboard/public/services/screenshot_mode/types.ts +++ /dev/null @@ -1,15 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; - -export interface DashboardScreenshotModeService { - isScreenshotMode: ScreenshotModePluginStart['isScreenshotMode']; - getScreenshotContext: ScreenshotModePluginStart['getScreenshotContext']; -} diff --git a/src/plugins/dashboard/public/services/serverless/serverless_service.stub.ts b/src/plugins/dashboard/public/services/serverless/serverless_service.stub.ts deleted file mode 100644 index 09b8fea6e9c14..0000000000000 --- a/src/plugins/dashboard/public/services/serverless/serverless_service.stub.ts +++ /dev/null @@ -1,17 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardServerlessService } from './types'; - -export type ServerlessServiceFactory = PluginServiceFactory; - -export const serverlessServiceFactory: ServerlessServiceFactory = () => { - return {}; -}; diff --git a/src/plugins/dashboard/public/services/serverless/serverless_service.ts b/src/plugins/dashboard/public/services/serverless/serverless_service.ts deleted file mode 100644 index c9991f1b7cfdc..0000000000000 --- a/src/plugins/dashboard/public/services/serverless/serverless_service.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardStartDependencies } from '../../plugin'; -import { DashboardServerlessService } from './types'; - -export type ServerlessServiceFactory = KibanaPluginServiceFactory< - DashboardServerlessService, - DashboardStartDependencies ->; - -export const serverlessServiceFactory: ServerlessServiceFactory = ({ startPlugins }) => { - const { serverless } = startPlugins; - - return { setBreadcrumbs: serverless?.setBreadcrumbs }; -}; diff --git a/src/plugins/dashboard/public/services/serverless/types.ts b/src/plugins/dashboard/public/services/serverless/types.ts deleted file mode 100644 index 8d6223fd0b46d..0000000000000 --- a/src/plugins/dashboard/public/services/serverless/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { ServerlessPluginStart } from '@kbn/serverless/public'; - -export interface DashboardServerlessService { - setBreadcrumbs?: ServerlessPluginStart['setBreadcrumbs']; -} diff --git a/src/plugins/dashboard/public/services/settings/settings.stub.ts b/src/plugins/dashboard/public/services/settings/settings.stub.ts deleted file mode 100644 index e71f83662aefa..0000000000000 --- a/src/plugins/dashboard/public/services/settings/settings.stub.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { themeServiceMock } from '@kbn/core-theme-browser-mocks'; -import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks'; -import { DashboardSettingsService } from './types'; - -type SettingsServiceFactory = PluginServiceFactory; - -export const settingsServiceFactory: SettingsServiceFactory = () => { - return { - i18n: i18nServiceMock.createStartContract(), - uiSettings: uiSettingsServiceMock.createStartContract(), - theme: themeServiceMock.createStartContract(), - isProjectEnabledInLabs: jest.fn().mockReturnValue(true), - }; -}; diff --git a/src/plugins/dashboard/public/services/settings/settings_service.ts b/src/plugins/dashboard/public/services/settings/settings_service.ts deleted file mode 100644 index c21a9a4baec0b..0000000000000 --- a/src/plugins/dashboard/public/services/settings/settings_service.ts +++ /dev/null @@ -1,34 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardSettingsService } from './types'; - -export type SettingsServiceFactory = KibanaPluginServiceFactory< - DashboardSettingsService, - DashboardStartDependencies ->; - -export const settingsServiceFactory: SettingsServiceFactory = ({ coreStart, startPlugins }) => { - const { i18n, uiSettings, theme } = coreStart; - - const { - presentationUtil: { - labsService: { isProjectEnabled }, - }, - } = startPlugins; - - return { - i18n, - uiSettings, - theme, - isProjectEnabledInLabs: isProjectEnabled, - }; -}; diff --git a/src/plugins/dashboard/public/services/settings/types.ts b/src/plugins/dashboard/public/services/settings/types.ts deleted file mode 100644 index 475687988f880..0000000000000 --- a/src/plugins/dashboard/public/services/settings/types.ts +++ /dev/null @@ -1,18 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; -import type { PresentationLabsService } from '@kbn/presentation-util-plugin/public'; - -export interface DashboardSettingsService { - uiSettings: CoreStart['uiSettings']; - i18n: CoreStart['i18n']; - theme: CoreStart['theme']; - isProjectEnabledInLabs: PresentationLabsService['isProjectEnabled']; -} diff --git a/src/plugins/dashboard/public/services/share/share.stub.ts b/src/plugins/dashboard/public/services/share/share.stub.ts deleted file mode 100644 index 3189e7cfd933a..0000000000000 --- a/src/plugins/dashboard/public/services/share/share.stub.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardShareService } from './types'; - -type ShareServiceFactory = PluginServiceFactory; - -export const shareServiceFactory: ShareServiceFactory = () => { - const pluginMock = sharePluginMock.createStartContract(); - - return { - url: pluginMock.url, - toggleShareContextMenu: pluginMock.toggleShareContextMenu, - }; -}; diff --git a/src/plugins/dashboard/public/services/share/share_services.ts b/src/plugins/dashboard/public/services/share/share_services.ts deleted file mode 100644 index 0e280c9ae8077..0000000000000 --- a/src/plugins/dashboard/public/services/share/share_services.ts +++ /dev/null @@ -1,29 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardShareService } from './types'; - -export type ShareServiceFactory = KibanaPluginServiceFactory< - DashboardShareService, - DashboardStartDependencies ->; - -export const shareServiceFactory: ShareServiceFactory = ({ startPlugins }) => { - const { share } = startPlugins; - if (!share) return {}; - - const { toggleShareContextMenu, url } = share; - - return { - url, - toggleShareContextMenu, - }; -}; diff --git a/src/plugins/dashboard/public/services/share/types.ts b/src/plugins/dashboard/public/services/share/types.ts deleted file mode 100644 index 8b21c741c1868..0000000000000 --- a/src/plugins/dashboard/public/services/share/types.ts +++ /dev/null @@ -1,15 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { SharePluginStart } from '@kbn/share-plugin/public'; - -export interface DashboardShareService { - url?: SharePluginStart['url']; - toggleShareContextMenu?: SharePluginStart['toggleShareContextMenu']; -} diff --git a/src/plugins/dashboard/public/services/spaces/spaces.stub.ts b/src/plugins/dashboard/public/services/spaces/spaces.stub.ts deleted file mode 100644 index 6c85c6158dfe5..0000000000000 --- a/src/plugins/dashboard/public/services/spaces/spaces.stub.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks'; -import { DashboardSpacesService } from './types'; - -type SpacesServiceFactory = PluginServiceFactory; - -export const spacesServiceFactory: SpacesServiceFactory = () => { - const pluginMock = spacesPluginMock.createStartContract(); - - return { - getActiveSpace$: pluginMock.getActiveSpace$, - getLegacyUrlConflict: pluginMock.ui.components.getLegacyUrlConflict, - redirectLegacyUrl: pluginMock.ui.redirectLegacyUrl, - }; -}; diff --git a/src/plugins/dashboard/public/services/spaces/spaces_service.ts b/src/plugins/dashboard/public/services/spaces/spaces_service.ts deleted file mode 100644 index 33df8d1db87d0..0000000000000 --- a/src/plugins/dashboard/public/services/spaces/spaces_service.ts +++ /dev/null @@ -1,34 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardSpacesService } from './types'; - -export type SpacesServiceFactory = KibanaPluginServiceFactory< - DashboardSpacesService, - DashboardStartDependencies ->; -export const spacesServiceFactory: SpacesServiceFactory = ({ startPlugins }) => { - const { spaces } = startPlugins; - if (!spaces || !spaces.ui) return {}; - - const { - getActiveSpace$, - ui: { - components: { getLegacyUrlConflict }, - redirectLegacyUrl, - }, - } = spaces; - return { - getActiveSpace$, - getLegacyUrlConflict, - redirectLegacyUrl, - }; -}; diff --git a/src/plugins/dashboard/public/services/spaces/types.ts b/src/plugins/dashboard/public/services/spaces/types.ts deleted file mode 100644 index 26330e6824339..0000000000000 --- a/src/plugins/dashboard/public/services/spaces/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; - -export interface DashboardSpacesService { - getActiveSpace$?: SpacesPluginStart['getActiveSpace$']; - getLegacyUrlConflict?: SpacesPluginStart['ui']['components']['getLegacyUrlConflict']; - redirectLegacyUrl?: SpacesPluginStart['ui']['redirectLegacyUrl']; -} diff --git a/src/plugins/dashboard/public/services/types.ts b/src/plugins/dashboard/public/services/types.ts deleted file mode 100644 index abfa5942d7a62..0000000000000 --- a/src/plugins/dashboard/public/services/types.ts +++ /dev/null @@ -1,92 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginInitializerContext } from '@kbn/core/public'; -import { KibanaPluginServiceParams } from '@kbn/presentation-util-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; -import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; - -import { DashboardStartDependencies } from '../plugin'; -import { DashboardAnalyticsService } from './analytics/types'; -import { DashboardApplicationService } from './application/types'; -import { DashboardChromeService } from './chrome/types'; -import { DashboardCoreContextService } from './core_context/types'; -import { DashboardCustomBrandingService } from './custom_branding/types'; -import { DashboardCapabilitiesService } from './dashboard_capabilities/types'; -import { DashboardContentManagementService } from './dashboard_content_management/types'; -import { DashboardBackupServiceType } from './dashboard_backup/types'; -import { DashboardDataService } from './data/types'; -import { DashboardDataViewEditorService } from './data_view_editor/types'; -import { DashboardDocumentationLinksService } from './documentation_links/types'; -import { DashboardEmbeddableService } from './embeddable/types'; -import { DashboardHTTPService } from './http/types'; -import { DashboardI18nService } from './i18n/types'; -import { DashboardInitializerContextService } from './initializer_context/types'; -import { DashboardNavigationService } from './navigation/types'; -import { DashboardNotificationsService } from './notifications/types'; -import { DashboardOverlaysService } from './overlays/types'; -import { DashboardSavedObjectsTaggingService } from './saved_objects_tagging/types'; -import { DashboardScreenshotModeService } from './screenshot_mode/types'; -import { DashboardSettingsService } from './settings/types'; -import { DashboardShareService } from './share/types'; -import { DashboardSpacesService } from './spaces/types'; -import { DashboardUrlForwardingService } from './url_forwarding/types'; -import { DashboardUsageCollectionService } from './usage_collection/types'; -import { DashboardVisualizationsService } from './visualizations/types'; -import { DashboardServerlessService } from './serverless/types'; -import { NoDataPageService } from './no_data_page/types'; -import { DashboardUiActionsService } from './ui_actions/types'; -import { ObservabilityAIAssistantService } from './observability_ai_assistant/types'; -import { DashboardUserProfileService } from './user_profile/types'; -import { DashboardRecentlyAccessedService } from './dashboard_recently_accessed/types'; -import { DashboardContentInsightsService } from './dashboard_content_insights/types'; -import { DashboardFavoritesService } from './dashboard_favorites/types'; - -export type DashboardPluginServiceParams = KibanaPluginServiceParams & { - initContext: PluginInitializerContext; // need a custom type so that initContext is a required parameter for initializerContext -}; -export interface DashboardServices { - dashboardBackup: DashboardBackupServiceType; - dashboardContentManagement: DashboardContentManagementService; - - analytics: DashboardAnalyticsService; - application: DashboardApplicationService; - chrome: DashboardChromeService; - coreContext: DashboardCoreContextService; - dashboardCapabilities: DashboardCapabilitiesService; - data: DashboardDataService; - dataViewEditor: DashboardDataViewEditorService; // this service is used only for the no data state - documentationLinks: DashboardDocumentationLinksService; - embeddable: DashboardEmbeddableService; - http: DashboardHTTPService; - i18n: DashboardI18nService; - initializerContext: DashboardInitializerContextService; - navigation: DashboardNavigationService; - notifications: DashboardNotificationsService; - overlays: DashboardOverlaysService; - savedObjectsTagging: DashboardSavedObjectsTaggingService; // TODO: make this optional in follow up - screenshotMode: DashboardScreenshotModeService; - settings: DashboardSettingsService; - share: DashboardShareService; // TODO: make this optional in follow up - spaces: DashboardSpacesService; // TODO: make this optional in follow up - urlForwarding: DashboardUrlForwardingService; - usageCollection: DashboardUsageCollectionService; // TODO: make this optional in follow up - visualizations: DashboardVisualizationsService; - customBranding: DashboardCustomBrandingService; - savedObjectsManagement: SavedObjectsManagementPluginStart; - contentManagement: ContentManagementPublicStart; - serverless: DashboardServerlessService; // TODO: make this optional in follow up - noDataPage: NoDataPageService; - uiActions: DashboardUiActionsService; - observabilityAIAssistant: ObservabilityAIAssistantService; // TODO: make this optional in follow up - userProfile: DashboardUserProfileService; - dashboardRecentlyAccessed: DashboardRecentlyAccessedService; - dashboardContentInsights: DashboardContentInsightsService; - dashboardFavorites: DashboardFavoritesService; -} diff --git a/src/plugins/dashboard/public/services/ui_actions/types.ts b/src/plugins/dashboard/public/services/ui_actions/types.ts deleted file mode 100644 index 94f931ba9fc1d..0000000000000 --- a/src/plugins/dashboard/public/services/ui_actions/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; - -export interface DashboardUiActionsService { - getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions']; -} diff --git a/src/plugins/dashboard/public/services/ui_actions/ui_actions_service.stub.ts b/src/plugins/dashboard/public/services/ui_actions/ui_actions_service.stub.ts deleted file mode 100644 index 971b143933610..0000000000000 --- a/src/plugins/dashboard/public/services/ui_actions/ui_actions_service.stub.ts +++ /dev/null @@ -1,19 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardUiActionsService } from './types'; - -export type UIActionsServiceFactory = PluginServiceFactory; - -export const uiActionsServiceFactory: UIActionsServiceFactory = () => { - const pluginMock = uiActionsPluginMock.createStartContract(); - return { getTriggerCompatibleActions: pluginMock.getTriggerCompatibleActions }; -}; diff --git a/src/plugins/dashboard/public/services/ui_actions/ui_actions_service.ts b/src/plugins/dashboard/public/services/ui_actions/ui_actions_service.ts deleted file mode 100644 index 363082473df94..0000000000000 --- a/src/plugins/dashboard/public/services/ui_actions/ui_actions_service.ts +++ /dev/null @@ -1,26 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardUiActionsService } from './types'; - -export type UsageCollectionServiceFactory = KibanaPluginServiceFactory< - DashboardUiActionsService, - DashboardStartDependencies ->; -export const uiActionsServiceFactory: UsageCollectionServiceFactory = ({ startPlugins }) => { - const { uiActions } = startPlugins; - if (!uiActions) return {}; - - const { getTriggerCompatibleActions } = uiActions; - return { - getTriggerCompatibleActions, - }; -}; diff --git a/src/plugins/dashboard/public/services/url_forwarding/types.ts b/src/plugins/dashboard/public/services/url_forwarding/types.ts deleted file mode 100644 index af032a10643a8..0000000000000 --- a/src/plugins/dashboard/public/services/url_forwarding/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; - -export interface DashboardUrlForwardingService { - navigateToLegacyKibanaUrl: UrlForwardingStart['navigateToLegacyKibanaUrl']; -} diff --git a/src/plugins/dashboard/public/services/url_forwarding/url_forwarding_service.ts b/src/plugins/dashboard/public/services/url_forwarding/url_forwarding_service.ts deleted file mode 100644 index 7ddc92b29c9e6..0000000000000 --- a/src/plugins/dashboard/public/services/url_forwarding/url_forwarding_service.ts +++ /dev/null @@ -1,26 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardUrlForwardingService } from './types'; - -export type UrlForwardingServiceFactory = KibanaPluginServiceFactory< - DashboardUrlForwardingService, - DashboardStartDependencies ->; -export const urlForwardingServiceFactory: UrlForwardingServiceFactory = ({ startPlugins }) => { - const { - urlForwarding: { navigateToLegacyKibanaUrl }, - } = startPlugins; - - return { - navigateToLegacyKibanaUrl, - }; -}; diff --git a/src/plugins/dashboard/public/services/url_forwarding/url_fowarding.stub.ts b/src/plugins/dashboard/public/services/url_forwarding/url_fowarding.stub.ts deleted file mode 100644 index 29792e40fd27b..0000000000000 --- a/src/plugins/dashboard/public/services/url_forwarding/url_fowarding.stub.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { urlForwardingPluginMock } from '@kbn/url-forwarding-plugin/public/mocks'; -import { DashboardUrlForwardingService } from './types'; - -type UrlForwardingServiceFactory = PluginServiceFactory; - -export const urlForwardingServiceFactory: UrlForwardingServiceFactory = () => { - const pluginMock = urlForwardingPluginMock.createStartContract(); - - return { - navigateToLegacyKibanaUrl: pluginMock.navigateToLegacyKibanaUrl, - }; -}; diff --git a/src/plugins/dashboard/public/services/usage_collection/types.ts b/src/plugins/dashboard/public/services/usage_collection/types.ts deleted file mode 100644 index 3aaaccbe9d8a2..0000000000000 --- a/src/plugins/dashboard/public/services/usage_collection/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; - -export interface DashboardUsageCollectionService { - reportUiCounter?: UsageCollectionStart['reportUiCounter']; -} diff --git a/src/plugins/dashboard/public/services/usage_collection/usage_collection.stub.ts b/src/plugins/dashboard/public/services/usage_collection/usage_collection.stub.ts deleted file mode 100644 index 1d3a3d119a7f2..0000000000000 --- a/src/plugins/dashboard/public/services/usage_collection/usage_collection.stub.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardUsageCollectionService } from './types'; - -type UsageCollectionServiceFactory = PluginServiceFactory; - -export const usageCollectionServiceFactory: UsageCollectionServiceFactory = () => { - const pluginMock = usageCollectionPluginMock.createSetupContract(); - - return { - reportUiCounter: pluginMock.reportUiCounter, - }; -}; diff --git a/src/plugins/dashboard/public/services/usage_collection/usage_collection_service.ts b/src/plugins/dashboard/public/services/usage_collection/usage_collection_service.ts deleted file mode 100644 index 1d95aa3c2c4b9..0000000000000 --- a/src/plugins/dashboard/public/services/usage_collection/usage_collection_service.ts +++ /dev/null @@ -1,26 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardUsageCollectionService } from './types'; - -export type UsageCollectionServiceFactory = KibanaPluginServiceFactory< - DashboardUsageCollectionService, - DashboardStartDependencies ->; -export const usageCollectionServiceFactory: UsageCollectionServiceFactory = ({ startPlugins }) => { - const { usageCollection } = startPlugins; - if (!usageCollection) return {}; - - const { reportUiCounter } = usageCollection; - return { - reportUiCounter, - }; -}; diff --git a/src/plugins/dashboard/public/services/user_profile/types.ts b/src/plugins/dashboard/public/services/user_profile/types.ts deleted file mode 100644 index 0de4bed4479eb..0000000000000 --- a/src/plugins/dashboard/public/services/user_profile/types.ts +++ /dev/null @@ -1,14 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardUserProfileService { - bulkGet: CoreStart['userProfile']['bulkGet']; -} diff --git a/src/plugins/dashboard/public/services/user_profile/user_profile_service.stub.ts b/src/plugins/dashboard/public/services/user_profile/user_profile_service.stub.ts deleted file mode 100644 index 37c20141deb62..0000000000000 --- a/src/plugins/dashboard/public/services/user_profile/user_profile_service.stub.ts +++ /dev/null @@ -1,18 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks'; -import { DashboardUserProfileService } from './types'; - -export type UserProfileServiceFactory = PluginServiceFactory; - -export const userProfileServiceFactory: UserProfileServiceFactory = () => { - return userProfileServiceMock.createStart(); -}; diff --git a/src/plugins/dashboard/public/services/user_profile/user_profile_service.ts b/src/plugins/dashboard/public/services/user_profile/user_profile_service.ts deleted file mode 100644 index d0585ff37d22f..0000000000000 --- a/src/plugins/dashboard/public/services/user_profile/user_profile_service.ts +++ /dev/null @@ -1,23 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardStartDependencies } from '../../plugin'; -import { DashboardUserProfileService } from './types'; - -export type UserProfileServiceFactory = KibanaPluginServiceFactory< - DashboardUserProfileService, - DashboardStartDependencies ->; - -export const userProfileServiceFactory: UserProfileServiceFactory = ({ coreStart }) => { - const { userProfile } = coreStart; - - return userProfile; -}; diff --git a/src/plugins/dashboard/public/services/visualizations/types.ts b/src/plugins/dashboard/public/services/visualizations/types.ts deleted file mode 100644 index 32a96c2cd183f..0000000000000 --- a/src/plugins/dashboard/public/services/visualizations/types.ts +++ /dev/null @@ -1,17 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { VisualizationsStart } from '@kbn/visualizations-plugin/public'; - -export interface DashboardVisualizationsService { - get: VisualizationsStart['get']; - getAliases: VisualizationsStart['getAliases']; - getByGroup: VisualizationsStart['getByGroup']; - showNewVisModal: VisualizationsStart['showNewVisModal']; -} diff --git a/src/plugins/dashboard/public/services/visualizations/visualizations.stub.ts b/src/plugins/dashboard/public/services/visualizations/visualizations.stub.ts deleted file mode 100644 index a149080963fa5..0000000000000 --- a/src/plugins/dashboard/public/services/visualizations/visualizations.stub.ts +++ /dev/null @@ -1,25 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { visualizationsPluginMock } from '@kbn/visualizations-plugin/public/mocks'; -import { DashboardVisualizationsService } from './types'; - -type HttpServiceFactory = PluginServiceFactory; - -export const visualizationsServiceFactory: HttpServiceFactory = () => { - const pluginMock = visualizationsPluginMock.createStartContract(); - - return { - get: pluginMock.get, - getAliases: pluginMock.getAliases, - getByGroup: pluginMock.getByGroup, - showNewVisModal: pluginMock.showNewVisModal, - }; -}; diff --git a/src/plugins/dashboard/public/services/visualizations/visualizations_service.ts b/src/plugins/dashboard/public/services/visualizations/visualizations_service.ts deleted file mode 100644 index c9aa8ed64f9c0..0000000000000 --- a/src/plugins/dashboard/public/services/visualizations/visualizations_service.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardVisualizationsService } from './types'; - -export type VisualizationsServiceFactory = KibanaPluginServiceFactory< - DashboardVisualizationsService, - DashboardStartDependencies ->; - -export const visualizationsServiceFactory: VisualizationsServiceFactory = ({ startPlugins }) => { - const { - visualizations: { get, getAliases, getByGroup, showNewVisModal }, - } = startPlugins; - - return { - get, - getAliases, - getByGroup, - showNewVisModal, - }; -}; diff --git a/src/plugins/dashboard/public/services/dashboard_capabilities/dashboard_capabilities_service.ts b/src/plugins/dashboard/public/utils/get_dashboard_capabilities.ts similarity index 56% rename from src/plugins/dashboard/public/services/dashboard_capabilities/dashboard_capabilities_service.ts rename to src/plugins/dashboard/public/utils/get_dashboard_capabilities.ts index 3909f839acb2f..083ca2fea43af 100644 --- a/src/plugins/dashboard/public/services/dashboard_capabilities/dashboard_capabilities_service.ts +++ b/src/plugins/dashboard/public/utils/get_dashboard_capabilities.ts @@ -7,31 +7,22 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardCapabilitiesService } from './types'; +import { DashboardCapabilities } from '../../common'; +import { coreServices } from '../services/kibana_services'; -export type DashboardCapabilitiesServiceFactory = KibanaPluginServiceFactory< - DashboardCapabilitiesService, - DashboardStartDependencies ->; -export const dashboardCapabilitiesServiceFactory: DashboardCapabilitiesServiceFactory = ({ - coreStart, -}) => { +export const getDashboardCapabilities = (): DashboardCapabilities => { const { application: { - capabilities: { dashboard, maps, visualize }, + capabilities: { dashboard }, }, - } = coreStart; + } = coreServices; return { show: Boolean(dashboard.show), saveQuery: Boolean(dashboard.saveQuery), createNew: Boolean(dashboard.createNew), - mapsCapabilities: { save: Boolean(maps?.save) }, createShortUrl: Boolean(dashboard.createShortUrl), showWriteControls: Boolean(dashboard.showWriteControls), - visualizeCapabilities: { save: Boolean(visualize?.save) }, storeSearchSession: Boolean(dashboard.storeSearchSession), }; }; diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index f289f4f725fe5..0f4059edfb0bf 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -41,14 +41,8 @@ "@kbn/core-application-browser", "@kbn/ebt-tools", "@kbn/shared-ux-button-exit-full-screen", - "@kbn/core-analytics-browser-mocks", - "@kbn/core-application-browser-mocks", "@kbn/analytics", "@kbn/safer-lodash-set", - "@kbn/core-notifications-browser-mocks", - "@kbn/core-overlays-browser-mocks", - "@kbn/core-theme-browser-mocks", - "@kbn/core-ui-settings-browser-mocks", "@kbn/task-manager-plugin", "@kbn/core-execution-context-common", "@kbn/core-custom-branding-browser", @@ -66,7 +60,6 @@ "@kbn/serverless", "@kbn/no-data-page-plugin", "@kbn/react-kibana-mount", - "@kbn/core-lifecycle-browser", "@kbn/logging", "@kbn/presentation-publishing", "@kbn/presentation-containers", @@ -75,9 +68,7 @@ "@kbn/shared-ux-utility", "@kbn/core-test-helpers-model-versions", "@kbn/deeplinks-analytics", - "@kbn/core-user-profile-browser-mocks", "@kbn/react-kibana-context-render", - "@kbn/core-i18n-browser-mocks", "@kbn/observability-ai-assistant-plugin", "@kbn/esql-utils", "@kbn/lens-embeddable-utils", @@ -87,7 +78,7 @@ "@kbn/content-management-content-insights-server", "@kbn/managed-content-badge", "@kbn/content-management-favorites-public", - "@kbn/core-http-browser-mocks", + "@kbn/core-custom-branding-browser-mocks", ], "exclude": ["target/**/*"] } diff --git a/src/plugins/links/jest_setup.ts b/src/plugins/links/jest_setup.ts index d573139bd6964..998abc0e9d9b7 100644 --- a/src/plugins/links/jest_setup.ts +++ b/src/plugins/links/jest_setup.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { setStubDashboardServices } from '@kbn/dashboard-plugin/public/services/mocks'; +import { setStubKibanaServices as setStubDashboardServices } from '@kbn/dashboard-plugin/public/services/mocks'; import { setStubKibanaServices } from './public/mocks'; setStubKibanaServices(); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx index 319ee812a3cf3..cefc008eba992 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx @@ -95,6 +95,20 @@ export const EvaluationSettings: React.FC = React.memo(() => { }, [setSelectedDatasetOptions] ); + const onDatasetCreateOption = useCallback( + (searchValue: string) => { + const normalizedSearchValue = searchValue.trim().toLowerCase(); + if (!normalizedSearchValue) { + return; + } + const newOption = { + label: searchValue, + }; + + setSelectedDatasetOptions([newOption]); + }, + [setSelectedDatasetOptions] + ); // Predictions // Connectors / Models @@ -244,6 +258,7 @@ export const EvaluationSettings: React.FC = React.memo(() => { options={datasetOptions} selectedOptions={selectedDatasetOptions} onChange={onDatasetOptionsChange} + onCreateOption={onDatasetCreateOption} compressed={true} /> diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts index b1adb6296b2a1..62902d0f14095 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts @@ -163,7 +163,8 @@ export const EVALUATOR_DATASET_LABEL = i18n.translate( export const LANGSMITH_DATASET_DESCRIPTION = i18n.translate( 'xpack.elasticAssistant.assistant.settings.evaluationSettings.langsmithDatasetDescription', { - defaultMessage: 'Name of dataset hosted on LangSmith to evaluate.', + defaultMessage: + 'Name of dataset hosted on LangSmith to evaluate. Must manually enter on cloud environments.', } ); diff --git a/x-pack/plugins/observability_solution/apm/public/hooks/use_dashboards_fetcher.ts b/x-pack/plugins/observability_solution/apm/public/hooks/use_dashboards_fetcher.ts index c463d07276a3a..8b1ccf301ae38 100644 --- a/x-pack/plugins/observability_solution/apm/public/hooks/use_dashboards_fetcher.ts +++ b/x-pack/plugins/observability_solution/apm/public/hooks/use_dashboards_fetcher.ts @@ -7,7 +7,7 @@ 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 { SearchDashboardsResponse } from '@kbn/dashboard-plugin/public'; import { ApmPluginStartDeps } from '../plugin'; import { FETCH_STATUS } from './use_fetcher'; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_dashboards_fetcher.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_dashboards_fetcher.ts index 0035fbc0a5889..c6be0f59efd05 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_dashboards_fetcher.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_dashboards_fetcher.ts @@ -6,7 +6,7 @@ */ import { useState, useEffect } from 'react'; -import type { SearchDashboardsResponse } from '@kbn/dashboard-plugin/public/services/dashboard_content_management/lib/find_dashboards'; +import type { SearchDashboardsResponse } from '@kbn/dashboard-plugin/public'; import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; diff --git a/x-pack/plugins/spaces/public/management/create_space/create_space_page.test.tsx b/x-pack/plugins/spaces/public/management/create_space/create_space_page.test.tsx index 14413b0b2f47b..f599622b99c37 100644 --- a/x-pack/plugins/spaces/public/management/create_space/create_space_page.test.tsx +++ b/x-pack/plugins/spaces/public/management/create_space/create_space_page.test.tsx @@ -13,6 +13,7 @@ import { act } from 'react-dom/test-utils'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; import { notificationServiceMock, scopedHistoryMock } from '@kbn/core/public/mocks'; +import { KibanaFeatureScope } from '@kbn/features-plugin/common'; import { KibanaFeature } from '@kbn/features-plugin/public'; import { featuresPluginMock } from '@kbn/features-plugin/public/mocks'; import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; @@ -46,6 +47,15 @@ featuresStart.getFeatures.mockResolvedValue([ app: [], category: DEFAULT_APP_CATEGORIES.kibana, privileges: null, + scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security], + }), + new KibanaFeature({ + id: 'feature-2', + name: 'feature 2', + app: [], + category: DEFAULT_APP_CATEGORIES.kibana, + privileges: null, + scope: [KibanaFeatureScope.Security], }), ]); @@ -388,6 +398,54 @@ describe('ManageSpacePage', () => { }); }); }); + + it('shows only features with space scope', async () => { + const spacesManager = spacesManagerMock.create(); + spacesManager.getSpace = jest.fn().mockResolvedValue({ + id: 'my-space', + name: 'Existing Space', + description: 'hey an existing space', + color: '#aabbcc', + initials: 'AB', + disabledFeatures: [], + }); + spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); + + const wrapper = mountWithIntl( + + ); + + await waitFor(() => { + wrapper.update(); + expect(spacesManager.getSpace).toHaveBeenCalledWith('my-space'); + }); + + expect(wrapper.state('features')).toEqual([ + new KibanaFeature({ + id: 'feature-1', + name: 'feature 1', + app: [], + category: DEFAULT_APP_CATEGORIES.kibana, + privileges: null, + scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security], + }), + ]); + }); }); function updateSpace( diff --git a/x-pack/plugins/spaces/public/management/create_space/create_space_page.tsx b/x-pack/plugins/spaces/public/management/create_space/create_space_page.tsx index 6b0edac5f7cd2..a67751b4d065e 100644 --- a/x-pack/plugins/spaces/public/management/create_space/create_space_page.tsx +++ b/x-pack/plugins/spaces/public/management/create_space/create_space_page.tsx @@ -22,6 +22,7 @@ import React, { Component } from 'react'; import type { Capabilities, NotificationsStart, ScopedHistory } from '@kbn/core/public'; import { SectionLoading } from '@kbn/es-ui-shared-plugin/public'; +import { KibanaFeatureScope } from '@kbn/features-plugin/common'; import type { FeaturesPluginStart, KibanaFeature } from '@kbn/features-plugin/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -89,6 +90,10 @@ export class CreateSpacePage extends Component { }; } + private filterSpaceFeatures = (features: KibanaFeature[]) => { + return features.filter((feature) => feature.scope?.includes(KibanaFeatureScope.Spaces)); + }; + public async componentDidMount() { if (!this.props.capabilities.spaces.manage) { return; @@ -101,7 +106,8 @@ export class CreateSpacePage extends Component { await this.loadSpace(spaceId, getFeatures()); } else { const features = await getFeatures(); - this.setState({ isLoading: false, features }); + + this.setState({ isLoading: false, features: this.filterSpaceFeatures(features) }); } } catch (e) { notifications.toasts.addError(e, { @@ -345,6 +351,7 @@ export class CreateSpacePage extends Component { spacesManager.getSpace(spaceId), featuresPromise, ]); + if (space) { if (onLoadSpace) { onLoadSpace(space); @@ -361,7 +368,7 @@ export class CreateSpacePage extends Component { !!space.initials && getSpaceInitials({ name: space.name }) !== space.initials, customAvatarColor: !!space.color && getSpaceColor({ name: space.name }) !== space.color, }, - features, + features: this.filterSpaceFeatures(features), originalSpace: space, isLoading: false, });