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 d074bcb98bd18..4556991816c99 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 @@ -10,8 +10,7 @@ import { useCallback, useMemo, useRef } from 'react'; import { AsyncSubject, defer, from, lastValueFrom, map, type Subscription } from 'rxjs'; -import type { IconType } from '@elastic/eui'; -import { COMMON_EMBEDDABLE_GROUPING, EmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; import { PresentationContainer } from '@kbn/presentation-containers'; import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import { VisGroups, type BaseVisType, type VisTypeAlias } from '@kbn/visualizations-plugin/public'; @@ -28,14 +27,6 @@ interface UseGetDashboardPanelsArgs { createNewVisType: (visType: BaseVisType | VisTypeAlias) => () => void; } -export interface FactoryGroup { - id: string; - appName: string; - icon?: IconType; - factories: EmbeddableFactory[]; - order: number; -} - const sortGroupPanelsByOrder = (panelGroups: T[]): T[] => { return panelGroups.sort( // larger number sorted to the top 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 e1bbef897d538..f82ad60929f24 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 @@ -13,13 +13,8 @@ import { buildMockDashboardApi } from '../../mocks'; import { EditorMenu } from './editor_menu'; import { DashboardContext } from '../../dashboard_api/use_dashboard_api'; -import { - embeddableService, - uiActionsService, - visualizationsService, -} from '../../services/kibana_services'; +import { uiActionsService, visualizationsService } from '../../services/kibana_services'; -jest.spyOn(embeddableService, 'getEmbeddableFactories').mockReturnValue(new Map().values()); jest.spyOn(uiActionsService, 'getTriggerCompatibleActions').mockResolvedValue([]); jest.spyOn(visualizationsService, 'getByGroup').mockReturnValue([]); jest.spyOn(visualizationsService, 'getAliases').mockReturnValue([]); diff --git a/src/plugins/dashboard/public/services/kibana_services.ts b/src/plugins/dashboard/public/services/kibana_services.ts index e3fde8c37c2a9..f7fe132ff7a38 100644 --- a/src/plugins/dashboard/public/services/kibana_services.ts +++ b/src/plugins/dashboard/public/services/kibana_services.ts @@ -13,7 +13,7 @@ import type { ContentManagementPublicStart } from '@kbn/content-management-plugi 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 { EmbeddableStart } from '@kbn/embeddable-plugin/public'; 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'; diff --git a/src/plugins/embeddable/common/lib/extract.ts b/src/plugins/embeddable/common/lib/extract.ts index f922ca3322236..5c7964dfed65b 100644 --- a/src/plugins/embeddable/common/lib/extract.ts +++ b/src/plugins/embeddable/common/lib/extract.ts @@ -14,7 +14,7 @@ import { extractBaseEmbeddableInput } from './migrate_base_input'; export const getExtractFunction = (embeddables: CommonEmbeddableStartContract) => { return (state: EmbeddableStateWithType) => { const enhancements = state.enhancements || {}; - const factory = embeddables.getEmbeddableFactory(state.type); + const factory = embeddables.getEmbeddableFactory?.(state.type); const baseResponse = extractBaseEmbeddableInput(state); let updatedInput = baseResponse.state; diff --git a/src/plugins/embeddable/common/lib/inject.ts b/src/plugins/embeddable/common/lib/inject.ts index 07acdd82d0a74..8435827df2555 100644 --- a/src/plugins/embeddable/common/lib/inject.ts +++ b/src/plugins/embeddable/common/lib/inject.ts @@ -15,7 +15,7 @@ import { injectBaseEmbeddableInput } from './migrate_base_input'; export const getInjectFunction = (embeddables: CommonEmbeddableStartContract) => { return (state: EmbeddableStateWithType, references: SavedObjectReference[]) => { const enhancements = state.enhancements || {}; - const factory = embeddables.getEmbeddableFactory(state.type); + const factory = embeddables.getEmbeddableFactory?.(state.type); let updatedInput = injectBaseEmbeddableInput(state, references); diff --git a/src/plugins/embeddable/common/lib/migrate.ts b/src/plugins/embeddable/common/lib/migrate.ts index 37a3678bc412c..cb1c7e3d32c68 100644 --- a/src/plugins/embeddable/common/lib/migrate.ts +++ b/src/plugins/embeddable/common/lib/migrate.ts @@ -16,7 +16,7 @@ export type MigrateFunction = (state: SerializableRecord, version: string) => Se export const getMigrateFunction = (embeddables: CommonEmbeddableStartContract) => { const migrateFn: MigrateFunction = (state: SerializableRecord, version: string) => { const enhancements = (state.enhancements as SerializableRecord) || {}; - const factory = embeddables.getEmbeddableFactory(state.type as string); + const factory = embeddables.getEmbeddableFactory?.(state.type as string); let updatedInput = baseEmbeddableMigrations[version] ? baseEmbeddableMigrations[version](state) diff --git a/src/plugins/embeddable/common/lib/telemetry.ts b/src/plugins/embeddable/common/lib/telemetry.ts index ea747d210166e..757ad762f350d 100644 --- a/src/plugins/embeddable/common/lib/telemetry.ts +++ b/src/plugins/embeddable/common/lib/telemetry.ts @@ -17,7 +17,7 @@ export const getTelemetryFunction = (embeddables: CommonEmbeddableStartContract) telemetryData: Record = {} ) => { const enhancements = state.enhancements || {}; - const factory = embeddables.getEmbeddableFactory(state.type); + const factory = embeddables.getEmbeddableFactory?.(state.type); let outputTelemetryData = telemetryBaseEmbeddableInput(state, telemetryData); if (factory) { diff --git a/src/plugins/embeddable/common/types.ts b/src/plugins/embeddable/common/types.ts index 951ecd9026ded..85bf9b59bfbe6 100644 --- a/src/plugins/embeddable/common/types.ts +++ b/src/plugins/embeddable/common/types.ts @@ -97,7 +97,7 @@ export interface EmbeddableRegistryDefinition< export type EmbeddablePersistableStateService = PersistableStateService; export interface CommonEmbeddableStartContract { - getEmbeddableFactory: ( + getEmbeddableFactory?: ( embeddableFactoryId: string ) => PersistableState & { isContainerType: boolean }; getEnhancement: (enhancementId: string) => PersistableState; diff --git a/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap b/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap deleted file mode 100644 index 6ef25188283e5..0000000000000 --- a/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`embeddable factory migrateToLatest returns list of all migrations 1`] = ` -Object { - "7.11.0": [Function], - "7.12.0": [Function], -} -`; diff --git a/src/plugins/embeddable/public/add_from_library/add_from_library_flyout.tsx b/src/plugins/embeddable/public/add_from_library/add_from_library_flyout.tsx index 3f68e5c2c08ab..eed7226a029ff 100644 --- a/src/plugins/embeddable/public/add_from_library/add_from_library_flyout.tsx +++ b/src/plugins/embeddable/public/add_from_library/add_from_library_flyout.tsx @@ -27,7 +27,6 @@ import { contentManagement, usageCollection, } from '../kibana_services'; -import { EmbeddableFactoryNotFoundError } from '../lib'; import { getAddFromLibraryType, useAddFromLibraryTypes } from './registry'; const runAddTelemetry = ( @@ -61,7 +60,12 @@ export const AddFromLibraryFlyout = ({ ) => { const libraryType = getAddFromLibraryType(type); if (!libraryType) { - core.notifications.toasts.addWarning(new EmbeddableFactoryNotFoundError(type).message); + core.notifications.toasts.addWarning( + i18n.translate('embeddableApi.addPanel.typeNotFound', { + defaultMessage: 'Unable to load type: {type}', + values: { type }, + }) + ); return; } diff --git a/src/plugins/embeddable/public/enhancements/registry.ts b/src/plugins/embeddable/public/enhancements/registry.ts new file mode 100644 index 0000000000000..8fbd155e5c57a --- /dev/null +++ b/src/plugins/embeddable/public/enhancements/registry.ts @@ -0,0 +1,51 @@ +/* + * Copyright 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 { identity } from 'lodash'; +import { SerializableRecord } from '@kbn/utility-types'; +import { EnhancementRegistryDefinition, EnhancementRegistryItem } from './types'; + +export class EnhancementsRegistry { + private registry: Map = new Map(); + + public registerEnhancement = (enhancement: EnhancementRegistryDefinition) => { + if (this.registry.has(enhancement.id)) { + throw new Error(`enhancement with id ${enhancement.id} already exists in the registry`); + } + this.registry.set(enhancement.id, { + id: enhancement.id, + telemetry: enhancement.telemetry || ((state, stats) => stats), + inject: enhancement.inject || identity, + extract: + enhancement.extract || + ((state: SerializableRecord) => { + return { state, references: [] }; + }), + migrations: enhancement.migrations || {}, + }); + }; + + public getEnhancements = (): EnhancementRegistryItem[] => { + return Array.from(this.registry.values()); + }; + + public getEnhancement = (id: string): EnhancementRegistryItem => { + return ( + this.registry.get(id) || { + id: 'unknown', + telemetry: (state, stats) => stats, + inject: identity, + extract: (state: SerializableRecord) => { + return { state, references: [] }; + }, + migrations: {}, + } + ); + }; +} diff --git a/src/plugins/embeddable/public/enhancements/types.ts b/src/plugins/embeddable/public/enhancements/types.ts new file mode 100644 index 0000000000000..289772bef9cec --- /dev/null +++ b/src/plugins/embeddable/public/enhancements/types.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", 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 { SerializableRecord } from '@kbn/utility-types'; +import { PersistableState, PersistableStateDefinition } from '@kbn/kibana-utils-plugin/common'; + +export interface EnhancementRegistryDefinition

+ extends PersistableStateDefinition

{ + id: string; +} + +export interface EnhancementRegistryItem

+ extends PersistableState

{ + id: string; +} diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index 2cc322940a77b..4ec1c43df2dbb 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -17,13 +17,10 @@ export { CELL_VALUE_TRIGGER, contextMenuTrigger, CONTEXT_MENU_TRIGGER, - defaultEmbeddableFactoryProvider, Embeddable, - EmbeddableFactoryNotFoundError, EmbeddableStateTransfer, ErrorEmbeddable, isContextMenuTriggerContext, - isExplicitInputWithAttributes, isMultiValueClickTriggerContext, isRangeSelectTriggerContext, isRowClickTriggerContext, @@ -37,7 +34,6 @@ export { PANEL_BADGE_TRIGGER, PANEL_HOVER_TRIGGER, PANEL_NOTIFICATION_TRIGGER, - runEmbeddableFactoryMigrations, SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER, ViewMode, @@ -47,26 +43,17 @@ export type { ChartActionContext, EmbeddableContext, EmbeddableEditorState, - EmbeddableFactory, - EmbeddableFactoryDefinition, EmbeddableInput, - EmbeddableInstanceConfiguration, EmbeddableOutput, EmbeddablePackageState, IEmbeddable, MultiValueClickContext, - OutputSpec, PropertySpec, RangeSelectContext, ValueClickContext, } from './lib'; -export type { - EmbeddableSetup, - EmbeddableSetupDependencies, - EmbeddableStart, - EmbeddableStartDependencies, -} from './plugin'; -export type { EnhancementRegistryDefinition } from './types'; +export type { EmbeddableSetup, EmbeddableStart } from './types'; +export type { EnhancementRegistryDefinition } from './enhancements/types'; export { ReactEmbeddableRenderer, diff --git a/src/plugins/embeddable/public/kibana_services.ts b/src/plugins/embeddable/public/kibana_services.ts index c7fe839c1dd0e..51dc61599d7a8 100644 --- a/src/plugins/embeddable/public/kibana_services.ts +++ b/src/plugins/embeddable/public/kibana_services.ts @@ -11,7 +11,7 @@ import { BehaviorSubject } from 'rxjs'; import { CoreStart } from '@kbn/core/public'; -import { EmbeddableStart, EmbeddableStartDependencies } from '.'; +import { EmbeddableStart, EmbeddableStartDependencies } from './types'; export let core: CoreStart; export let embeddableStart: EmbeddableStart; diff --git a/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts b/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts deleted file mode 100644 index 13baf96962a3a..0000000000000 --- a/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts +++ /dev/null @@ -1,65 +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 { SavedObjectAttributes } from '@kbn/core/public'; -import type { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common'; -import { EmbeddableFactory } from './embeddable_factory'; -import { EmbeddableStateWithType } from '../../../common/types'; -import { EmbeddableFactoryDefinition } from './embeddable_factory_definition'; -import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; -import { runEmbeddableFactoryMigrations } from '../factory_migrations/run_factory_migrations'; - -export const defaultEmbeddableFactoryProvider = < - I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput, - E extends IEmbeddable = IEmbeddable, - T extends FinderAttributes = SavedObjectAttributes ->( - def: EmbeddableFactoryDefinition -): EmbeddableFactory => { - if (def.migrations && !def.latestVersion) { - throw new Error( - 'To run clientside Embeddable migrations a latest version key is required on the factory' - ); - } - - const factory: EmbeddableFactory = { - ...def, - latestVersion: def.latestVersion, - isContainerType: def.isContainerType ?? false, - canCreateNew: def.canCreateNew ? def.canCreateNew.bind(def) : () => true, - getDefaultInput: def.getDefaultInput ? def.getDefaultInput.bind(def) : () => ({}), - getExplicitInput: def.getExplicitInput - ? def.getExplicitInput.bind(def) - : () => Promise.resolve({}), - createFromSavedObject: def.createFromSavedObject - ? def.createFromSavedObject.bind(def) - : (savedObjectId: string, input: Partial, parent?: unknown) => { - throw new Error(`Creation from saved object not supported by type ${def.type}`); - }, - create: (...args) => { - const [initialInput, ...otherArgs] = args; - const { input } = runEmbeddableFactoryMigrations(initialInput, def); - const createdEmbeddable = def.create.bind(def)(input as I, ...otherArgs); - return createdEmbeddable; - }, - type: def.type, - isEditable: def.isEditable.bind(def), - getDisplayName: def.getDisplayName.bind(def), - getDescription: def.getDescription ? def.getDescription.bind(def) : () => '', - getIconType: def.getIconType ? def.getIconType.bind(def) : () => 'empty', - savedObjectMetaData: def.savedObjectMetaData, - telemetry: def.telemetry || ((state, stats) => stats), - inject: def.inject || ((state: EmbeddableStateWithType) => state), - extract: def.extract || ((state: EmbeddableStateWithType) => ({ state, references: [] })), - migrations: def.migrations || {}, - grouping: def.grouping, - }; - return factory; -}; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts deleted file mode 100644 index e363af639e252..0000000000000 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { SavedObjectMetaData } from '@kbn/saved-objects-finder-plugin/public'; -import { PersistableState } from '@kbn/kibana-utils-plugin/common'; -import type { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common'; -import { UiActionsPresentableGrouping } from '@kbn/ui-actions-plugin/public'; -import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; -import { ErrorEmbeddable } from './error_embeddable'; -import { PropertySpec } from '../types'; -import { EmbeddableStateWithType } from '../../../common/types'; - -export interface EmbeddableInstanceConfiguration { - id: string; - savedObjectId?: string; -} - -export interface OutputSpec { - [key: string]: PropertySpec; -} - -export interface ExplicitInputWithAttributes { - newInput: Partial; - attributes?: unknown; -} - -export const isExplicitInputWithAttributes = ( - value: ExplicitInputWithAttributes | Partial -): value is ExplicitInputWithAttributes => { - return Boolean((value as ExplicitInputWithAttributes).newInput); -}; - -/** - * EmbeddableFactories create and initialize an embeddable instance - */ -export interface EmbeddableFactory< - TEmbeddableInput extends EmbeddableInput = EmbeddableInput, - TEmbeddableOutput extends EmbeddableOutput = EmbeddableOutput, - TEmbeddable extends IEmbeddable = IEmbeddable< - TEmbeddableInput, - TEmbeddableOutput - >, - TSavedObjectAttributes extends FinderAttributes = FinderAttributes -> extends PersistableState { - /** - * The version of this Embeddable factory. This will be used in the client side migration system - * to ensure that input from any source is compatible with the latest version of this embeddable. - * If the latest version is not defined, all clientside migrations will be skipped. If migrations - * are added to this factory but a latestVersion is not set, an error will be thrown on server start - */ - readonly latestVersion?: string; - - // A unique identified for this factory, which will be used to map an embeddable spec to - // a factory that can generate an instance of it. - readonly type: string; - - /** - * Returns whether the current user should be allowed to edit this type of - * embeddable. Most of the time this should be based off the capabilities service, hence it's async. - */ - readonly isEditable: () => Promise; - - readonly savedObjectMetaData?: SavedObjectMetaData; - - /** - * Indicates the grouping this factory should appear in a sub-menu. Example, this is used for grouping - * options in the editors menu in Dashboard for creating new embeddables - */ - readonly grouping?: UiActionsPresentableGrouping; - - /** - * True if is this factory create embeddables that are Containers. Used in the add panel to - * conditionally show whether these can be added to another container. It's just not - * supported right now, but once nested containers are officially supported we can probably get - * rid of this interface. - */ - readonly isContainerType: boolean; - - /** - * Returns a display name for this type of embeddable. Used in "Create new... " options - * in the add panel for containers. - */ - getDisplayName(): string; - - /** - * Returns an EUI Icon type to be displayed in a menu. - */ - getIconType(): string; - - /** - * Returns a description about the embeddable. - */ - getDescription(): string; - - /** - * If false, this type of embeddable can't be created with the "createNew" functionality. Instead, - * use createFromSavedObject, where an existing saved object must first exist. - */ - canCreateNew(): boolean; - - /** - * Can be used to get the default input, to be passed in to during the creation process. Default - * input will not be stored in a parent container, so all inherited input from a container will trump - * default input parameters. - * @param partial - */ - getDefaultInput(partial: Partial): Partial; - - /** - * Can be used to request explicit input from the user, to be passed in to `EmbeddableFactory:create`. - * Explicit input is stored on the parent container for this embeddable. It overrides all inherited - * input passed down from the parent container. - * - * Can be used to edit an embeddable by re-requesting explicit input. Initial input can be provided to allow the editor to show the current state. - * - * If saved object information is needed for creation use-cases, getExplicitInput can also return an unknown typed attributes object which will be passed - * into the container's addNewEmbeddable function. - */ - getExplicitInput( - initialInput?: Partial, - parent?: unknown - ): Promise | ExplicitInputWithAttributes>; - - /** - * Creates a new embeddable instance based off the saved object id. - * @param savedObjectId - * @param input - some input may come from a parent, or user, if it's not stored with the saved object. For example, the time - * range of the parent container. - * @param parent - */ - createFromSavedObject( - savedObjectId: string, - input: Partial, - parent?: unknown - ): Promise; - - /** - * Creates an Embeddable instance, running the inital input through all registered migrations. Resolves to undefined if a new Embeddable - * cannot be directly created and the user will instead be redirected elsewhere. - */ - create( - initialInput: TEmbeddableInput, - parent?: unknown - ): Promise; - - order?: number; -} diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts deleted file mode 100644 index e718bd4de9288..0000000000000 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common'; -import { IEmbeddable } from './i_embeddable'; -import { EmbeddableFactory } from './embeddable_factory'; -import { EmbeddableInput, EmbeddableOutput } from '..'; - -export type EmbeddableFactoryDefinition< - I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput, - E extends IEmbeddable = IEmbeddable, - T extends FinderAttributes = FinderAttributes -> = - // Required parameters - Pick< - EmbeddableFactory, - 'create' | 'type' | 'latestVersion' | 'isEditable' | 'getDisplayName' - > & - // Optional parameters - Partial< - Pick< - EmbeddableFactory, - | 'createFromSavedObject' - | 'isContainerType' - | 'getExplicitInput' - | 'savedObjectMetaData' - | 'canCreateNew' - | 'getDefaultInput' - | 'telemetry' - | 'extract' - | 'inject' - | 'migrations' - | 'grouping' - | 'getIconType' - | 'getDescription' - > - >; diff --git a/src/plugins/embeddable/public/lib/embeddables/index.ts b/src/plugins/embeddable/public/lib/embeddables/index.ts index 16b4fb7413769..029a653a9f1c6 100644 --- a/src/plugins/embeddable/public/lib/embeddables/index.ts +++ b/src/plugins/embeddable/public/lib/embeddables/index.ts @@ -8,10 +8,7 @@ */ export * from '../../../common/lib/saved_object_embeddable'; -export * from './default_embeddable_factory_provider'; export { Embeddable } from './embeddable'; export { EmbeddableErrorHandler } from './embeddable_error_handler'; -export * from './embeddable_factory'; -export * from './embeddable_factory_definition'; export { ErrorEmbeddable } from './error_embeddable'; export type { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; diff --git a/src/plugins/embeddable/public/lib/errors.test.ts b/src/plugins/embeddable/public/lib/errors.test.ts index 48c38b25c2621..ebac7371acd51 100644 --- a/src/plugins/embeddable/public/lib/errors.test.ts +++ b/src/plugins/embeddable/public/lib/errors.test.ts @@ -8,7 +8,7 @@ */ import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { PanelNotFoundError, EmbeddableFactoryNotFoundError } from './errors'; +import { PanelNotFoundError } from './errors'; describe('IncompatibleActionError', () => { test('is instance of error', () => { @@ -33,15 +33,3 @@ describe('PanelNotFoundError', () => { expect(error.code).toBe('PANEL_NOT_FOUND'); }); }); - -describe('EmbeddableFactoryNotFoundError', () => { - test('is instance of error', () => { - const error = new EmbeddableFactoryNotFoundError('type1'); - expect(error).toBeInstanceOf(Error); - }); - - test('has EMBEDDABLE_FACTORY_NOT_FOUND code', () => { - const error = new EmbeddableFactoryNotFoundError('type1'); - expect(error.code).toBe('EMBEDDABLE_FACTORY_NOT_FOUND'); - }); -}); diff --git a/src/plugins/embeddable/public/lib/errors.ts b/src/plugins/embeddable/public/lib/errors.ts index 0ee0cbc2868bb..79f61f7fbe471 100644 --- a/src/plugins/embeddable/public/lib/errors.ts +++ b/src/plugins/embeddable/public/lib/errors.ts @@ -33,18 +33,3 @@ export class PanelIncompatibleError extends Error { ); } } - -export class EmbeddableFactoryNotFoundError extends Error { - code = 'EMBEDDABLE_FACTORY_NOT_FOUND'; - - constructor(type: string) { - super( - i18n.translate('embeddableApi.errors.embeddableFactoryNotFound', { - defaultMessage: `{type} can't be loaded. Please upgrade to the default distribution of Elasticsearch and Kibana with the appropriate license.`, - values: { - type, - }, - }) - ); - } -} diff --git a/src/plugins/embeddable/public/lib/factory_migrations/run_factory_migrations.test.ts b/src/plugins/embeddable/public/lib/factory_migrations/run_factory_migrations.test.ts deleted file mode 100644 index aa94bc1695284..0000000000000 --- a/src/plugins/embeddable/public/lib/factory_migrations/run_factory_migrations.test.ts +++ /dev/null @@ -1,77 +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 { EmbeddableInput } from '../embeddables'; -import { runEmbeddableFactoryMigrations } from './run_factory_migrations'; - -describe('Run embeddable factory migrations', () => { - interface TestInputTypeVersion009 extends EmbeddableInput { - version: '0.0.9'; - keyThatAlwaysExists: string; - keyThatGetsRemoved: string; - } - interface TestInputTypeVersion100 extends EmbeddableInput { - version: '1.0.0'; - id: string; - keyThatAlwaysExists: string; - keyThatGetsAdded: string; - } - - const migrations = { - '1.0.0': (input: TestInputTypeVersion009): TestInputTypeVersion100 => { - const newInput: TestInputTypeVersion100 = { - id: input.id, - version: '1.0.0', - keyThatAlwaysExists: input.keyThatAlwaysExists, - keyThatGetsAdded: 'I just got born', - }; - return newInput; - }, - }; - - it('should return the initial input and migrationRun=false if the current version is the latest', () => { - const initialInput: TestInputTypeVersion100 = { - id: 'superId', - version: '1.0.0', - keyThatAlwaysExists: 'Inside Problems', - keyThatGetsAdded: 'Oh my - I just got born', - }; - - const factory = { - latestVersion: '1.0.0', - migrations, - }; - - const result = runEmbeddableFactoryMigrations(initialInput, factory); - - expect(result.input).toBe(initialInput); - expect(result.migrationRun).toBe(false); - }); - - it('should return migrated input and migrationRun=true if version does not match latestVersion', () => { - const initialInput: TestInputTypeVersion009 = { - id: 'superId', - version: '0.0.9', - keyThatAlwaysExists: 'Inside Problems', - keyThatGetsRemoved: 'juvenile plumage', - }; - - const factory = { - latestVersion: '1.0.0', - migrations, - }; - - const result = runEmbeddableFactoryMigrations(initialInput, factory); - - expect(result.migrationRun).toBe(true); - expect(result.input.version).toBe('1.0.0'); - expect((result.input as unknown as TestInputTypeVersion009).keyThatGetsRemoved).toBeUndefined(); - expect(result.input.keyThatGetsAdded).toEqual('I just got born'); - }); -}); diff --git a/src/plugins/embeddable/public/lib/factory_migrations/run_factory_migrations.ts b/src/plugins/embeddable/public/lib/factory_migrations/run_factory_migrations.ts deleted file mode 100644 index 45350dec95306..0000000000000 --- a/src/plugins/embeddable/public/lib/factory_migrations/run_factory_migrations.ts +++ /dev/null @@ -1,47 +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 { cloneDeep } from 'lodash'; -import compare from 'semver/functions/compare'; - -import { migrateToLatest } from '@kbn/kibana-utils-plugin/common'; -import { EmbeddableFactory, EmbeddableInput } from '../embeddables'; - -/** - * A helper function that migrates an Embeddable Input to its latest version. Note that this function - * only runs the embeddable factory's migrations. - */ -export const runEmbeddableFactoryMigrations = ( - initialInput: { version?: string }, - factory: { migrations?: EmbeddableFactory['migrations']; latestVersion?: string } -): { input: ToType; migrationRun: boolean } => { - if (!factory.latestVersion) { - return { input: initialInput as unknown as ToType, migrationRun: false }; - } - - // any embeddable with no version set is considered to require all clientside migrations so we default to 0.0.0 - const inputVersion = initialInput.version ?? '0.0.0'; - const migrationRun = compare(inputVersion, factory.latestVersion, true) !== 0; - - // return early to avoid extra operations when there are no migrations to run. - if (!migrationRun) return { input: initialInput as unknown as ToType, migrationRun }; - - const factoryMigrations = - typeof factory?.migrations === 'function' ? factory?.migrations() : factory?.migrations || {}; - const migratedInput = migrateToLatest( - factoryMigrations ?? {}, - { - state: cloneDeep(initialInput), - version: inputVersion, - }, - true - ); - migratedInput.version = factory.latestVersion; - return { input: migratedInput as ToType, migrationRun }; -}; diff --git a/src/plugins/embeddable/public/lib/index.ts b/src/plugins/embeddable/public/lib/index.ts index 642af73713dc4..60f8a3638816d 100644 --- a/src/plugins/embeddable/public/lib/index.ts +++ b/src/plugins/embeddable/public/lib/index.ts @@ -12,4 +12,3 @@ export * from './embeddables'; export * from './types'; export * from './triggers'; export * from './state_transfer'; -export * from './factory_migrations/run_factory_migrations'; diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx index 2208abd97e5d6..4adab25eea345 100644 --- a/src/plugins/embeddable/public/mocks.tsx +++ b/src/plugins/embeddable/public/mocks.tsx @@ -19,17 +19,17 @@ import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management- import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { EmbeddableStateTransfer } from '.'; +import { setKibanaServices } from './kibana_services'; +import { EmbeddablePublicPlugin } from './plugin'; +import { registerReactEmbeddableFactory } from './react_embeddable_system'; +import { registerAddFromLibraryType } from './add_from_library/registry'; import { EmbeddableSetup, EmbeddableSetupDependencies, EmbeddableStart, EmbeddableStartDependencies, - EmbeddableStateTransfer, -} from '.'; -import { setKibanaServices } from './kibana_services'; -import { EmbeddablePublicPlugin } from './plugin'; -import { registerReactEmbeddableFactory } from './react_embeddable_system'; -import { registerAddFromLibraryType } from './add_from_library/registry'; +} from './types'; export type Setup = jest.Mocked; export type Start = jest.Mocked; @@ -48,7 +48,6 @@ const createSetupContract = (): Setup => { const setupContract: Setup = { registerAddFromLibraryType: jest.fn().mockImplementation(registerAddFromLibraryType), registerReactEmbeddableFactory: jest.fn().mockImplementation(registerReactEmbeddableFactory), - registerEmbeddableFactory: jest.fn(), registerEnhancement: jest.fn(), }; return setupContract; @@ -56,8 +55,6 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { const startContract: Start = { - getEmbeddableFactories: jest.fn(), - getEmbeddableFactory: jest.fn(), telemetry: jest.fn(), extract: jest.fn(), inject: jest.fn(), diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index 00a19f8e9f561..0b15d4afd034b 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -10,104 +10,6 @@ import { coreMock } from '@kbn/core/public/mocks'; import { testPlugin } from './tests/test_plugin'; -describe('embeddable factory', () => { - const coreSetup = coreMock.createSetup(); - const coreStart = coreMock.createStart(); - const { setup, doStart } = testPlugin(coreSetup, coreStart); - const start = doStart(); - const embeddableFactoryId = 'ID'; - const embeddableFactory = { - type: embeddableFactoryId, - create: jest.fn(), - getDisplayName: () => 'Test', - isEditable: () => Promise.resolve(true), - extract: jest.fn().mockImplementation((state) => ({ state, references: [] })), - inject: jest.fn().mockImplementation((state) => state), - telemetry: jest.fn().mockResolvedValue({}), - latestVersion: '7.11.0', - migrations: { '7.11.0': jest.fn().mockImplementation((state) => state) }, - } as any; - const embeddableState = { - id: embeddableFactoryId, - type: embeddableFactoryId, - my: 'state', - } as any; - - const containerEmbeddableFactoryId = 'CONTAINER'; - const containerEmbeddableFactory = { - type: containerEmbeddableFactoryId, - latestVersion: '1.0.0', - create: jest.fn(), - getDisplayName: () => 'Container', - isContainer: true, - isEditable: () => Promise.resolve(true), - extract: jest.fn().mockImplementation((state) => ({ state, references: [] })), - inject: jest.fn().mockImplementation((state) => state), - telemetry: jest.fn().mockResolvedValue({}), - migrations: { '7.12.0': jest.fn().mockImplementation((state) => state) }, - }; - - const containerState = { - id: containerEmbeddableFactoryId, - type: containerEmbeddableFactoryId, - some: 'state', - panels: [ - { - ...embeddableState, - }, - ], - } as any; - - setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory); - setup.registerEmbeddableFactory(containerEmbeddableFactoryId, containerEmbeddableFactory); - - test('cannot register embeddable factory with the same ID', async () => { - expect(() => - setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory) - ).toThrowError( - 'Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API.' - ); - }); - - test('embeddableFactory extract function gets called when calling embeddable extract', () => { - start.extract(embeddableState); - expect(embeddableFactory.extract).toBeCalledWith(embeddableState); - }); - - test('embeddableFactory inject function gets called when calling embeddable inject', () => { - start.inject(embeddableState, []); - expect(embeddableFactory.extract).toBeCalledWith(embeddableState); - }); - - test('embeddableFactory telemetry function gets called when calling embeddable telemetry', () => { - start.telemetry(embeddableState, {}); - expect(embeddableFactory.telemetry).toBeCalledWith(embeddableState, {}); - }); - - test('embeddableFactory migrate function gets called when calling embeddable migrate', () => { - start.getAllMigrations!()['7.11.0']!(embeddableState); - expect(embeddableFactory.migrations['7.11.0']).toBeCalledWith(embeddableState); - }); - - test('panels inside container get automatically migrated when migrating conta1iner', () => { - start.getAllMigrations!()['7.11.0']!(containerState); - expect(embeddableFactory.migrations['7.11.0']).toBeCalledWith(embeddableState); - }); - - test('migrateToLatest returns list of all migrations', () => { - const migrations = start.getAllMigrations(); - expect(migrations).toMatchSnapshot(); - }); - - test('migrateToLatest calls correct migrate functions', () => { - start.migrateToLatest!({ - state: embeddableState, - version: '7.11.0', - }); - expect(embeddableFactory.migrations['7.11.0']).toBeCalledWith(embeddableState); - }); -}); - describe('embeddable enhancements', () => { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index ef339e5bacc2c..84ea1676dd018 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -8,10 +8,6 @@ */ import { Subscription } from 'rxjs'; -import { identity } from 'lodash'; -import type { SerializableRecord } from '@kbn/utility-types'; -import { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { Start as InspectorStart } from '@kbn/inspector-plugin/public'; import { PluginInitializerContext, CoreSetup, @@ -20,26 +16,8 @@ import { PublicAppInfo, } from '@kbn/core/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; -import { migrateToLatest, PersistableStateService } from '@kbn/kibana-utils-plugin/common'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; -import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; -import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { - EmbeddableFactoryRegistry, - EnhancementsRegistry, - EnhancementRegistryDefinition, - EnhancementRegistryItem, -} from './types'; +import { migrateToLatest } from '@kbn/kibana-utils-plugin/common'; import { bootstrap } from './bootstrap'; -import { - EmbeddableFactory, - EmbeddableInput, - EmbeddableOutput, - defaultEmbeddableFactoryProvider, - IEmbeddable, -} from './lib'; -import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; import { EmbeddableStateTransfer } from './lib/state_transfer'; import { EmbeddableStateWithType, CommonEmbeddableStartContract } from '../common/types'; import { @@ -52,90 +30,19 @@ import { getAllMigrations } from '../common/lib/get_all_migrations'; import { setKibanaServices } from './kibana_services'; import { registerReactEmbeddableFactory } from './react_embeddable_system'; import { registerAddFromLibraryType } from './add_from_library/registry'; +import { EnhancementsRegistry } from './enhancements/registry'; +import { + EmbeddableSetup, + EmbeddableSetupDependencies, + EmbeddableStart, + EmbeddableStartDependencies, +} from './types'; -export interface EmbeddableSetupDependencies { - uiActions: UiActionsSetup; -} - -export interface EmbeddableStartDependencies { - uiActions: UiActionsStart; - inspector: InspectorStart; - usageCollection: UsageCollectionStart; - contentManagement: ContentManagementPublicStart; - savedObjectsManagement: SavedObjectsManagementPluginStart; - savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; -} - -export interface EmbeddableSetup { - /** - * Register a saved object type with the "Add from library" flyout. - * - * @example - * registerAddFromLibraryType({ - * onAdd: (container, savedObject) => { - * container.addNewPanel({ - * panelType: CONTENT_ID, - * initialState: savedObject.attributes, - * }); - * }, - * savedObjectType: MAP_SAVED_OBJECT_TYPE, - * savedObjectName: i18n.translate('xpack.maps.mapSavedObjectLabel', { - * defaultMessage: 'Map', - * }), - * getIconForSavedObject: () => APP_ICON, - * }); - */ - registerAddFromLibraryType: typeof registerAddFromLibraryType; - - /** - * Registers an async {@link ReactEmbeddableFactory} getter. - */ - registerReactEmbeddableFactory: typeof registerReactEmbeddableFactory; - - /** - * @deprecated use {@link registerReactEmbeddableFactory} instead. - */ - registerEmbeddableFactory: < - I extends EmbeddableInput, - O extends EmbeddableOutput, - E extends IEmbeddable = IEmbeddable - >( - id: string, - factory: EmbeddableFactoryDefinition - ) => () => EmbeddableFactory; - /** - * @deprecated - */ - registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; -} - -export interface EmbeddableStart extends PersistableStateService { - /** - * @deprecated use {@link registerReactEmbeddableFactory} instead. - */ - getEmbeddableFactory: < - I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput, - E extends IEmbeddable = IEmbeddable - >( - embeddableFactoryId: string - ) => EmbeddableFactory | undefined; - - /** - * @deprecated - */ - getEmbeddableFactories: () => IterableIterator; - getStateTransfer: (storage?: Storage) => EmbeddableStateTransfer; -} export class EmbeddablePublicPlugin implements Plugin { - private readonly embeddableFactoryDefinitions: Map = - new Map(); - private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map(); - private readonly enhancements: EnhancementsRegistry = new Map(); private stateTransferService: EmbeddableStateTransfer = {} as EmbeddableStateTransfer; - private isRegistryReady = false; private appList?: ReadonlyMap; private appListSubscription?: Subscription; + private enhancementsRegistry = new EnhancementsRegistry(); constructor(initializerContext: PluginInitializerContext) {} @@ -145,17 +52,11 @@ export class EmbeddablePublicPlugin implements Plugin { - this.embeddableFactories.set(def.type, defaultEmbeddableFactoryProvider(def)); - }); - this.appListSubscription = core.application.applications$.subscribe((appList) => { this.appList = appList; }); @@ -165,24 +66,19 @@ export class EmbeddablePublicPlugin implements Plugin getAllMigrations( - Array.from(this.embeddableFactories.values()), - Array.from(this.enhancements.values()), + [], + this.enhancementsRegistry.getEnhancements(), getMigrateFunction(commonContract) ); const embeddableStart: EmbeddableStart = { - getEmbeddableFactory: this.getEmbeddableFactory, - getEmbeddableFactories: this.getEmbeddableFactories, getStateTransfer: (storage?: Storage) => storage ? new EmbeddableStateTransfer( @@ -210,89 +106,4 @@ export class EmbeddablePublicPlugin implements Plugin { - if (this.enhancements.has(enhancement.id)) { - throw new Error(`enhancement with id ${enhancement.id} already exists in the registry`); - } - this.enhancements.set(enhancement.id, { - id: enhancement.id, - telemetry: enhancement.telemetry || ((state, stats) => stats), - inject: enhancement.inject || identity, - extract: - enhancement.extract || - ((state: SerializableRecord) => { - return { state, references: [] }; - }), - migrations: enhancement.migrations || {}, - }); - }; - - private getEnhancement = (id: string): EnhancementRegistryItem => { - return ( - this.enhancements.get(id) || { - id: 'unknown', - telemetry: (state, stats) => stats, - inject: identity, - extract: (state: SerializableRecord) => { - return { state, references: [] }; - }, - migrations: {}, - } - ); - }; - - private getEmbeddableFactories = () => { - this.ensureFactoriesExist(); - return this.embeddableFactories.values(); - }; - - private registerEmbeddableFactory = < - I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput, - E extends IEmbeddable = IEmbeddable - >( - embeddableFactoryId: string, - factory: EmbeddableFactoryDefinition - ): (() => EmbeddableFactory) => { - if (this.embeddableFactoryDefinitions.has(embeddableFactoryId)) { - throw new Error( - `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] already registered in Embeddables API.` - ); - } - this.embeddableFactoryDefinitions.set(embeddableFactoryId, factory); - - return () => { - return this.getEmbeddableFactory(embeddableFactoryId); - }; - }; - - private getEmbeddableFactory = < - I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput, - E extends IEmbeddable = IEmbeddable - >( - embeddableFactoryId: string - ): EmbeddableFactory => { - if (!this.isRegistryReady) { - throw new Error('Embeddable factories can only be retrieved after setup lifecycle.'); - } - this.ensureFactoryExists(embeddableFactoryId); - const factory = this.embeddableFactories.get(embeddableFactoryId); - - return factory as EmbeddableFactory; - }; - - // These two functions are only to support legacy plugins registering factories after the start lifecycle. - private ensureFactoriesExist = () => { - this.embeddableFactoryDefinitions.forEach((def) => this.ensureFactoryExists(def.type)); - }; - - private ensureFactoryExists = (type: string) => { - if (!this.embeddableFactories.get(type)) { - const def = this.embeddableFactoryDefinitions.get(type); - if (!def) return; - this.embeddableFactories.set(type, defaultEmbeddableFactoryProvider(def)); - } - }; } diff --git a/src/plugins/embeddable/public/tests/test_plugin.ts b/src/plugins/embeddable/public/tests/test_plugin.ts index 4cd20c4863b2b..ffb7d73b9bed6 100644 --- a/src/plugins/embeddable/public/tests/test_plugin.ts +++ b/src/plugins/embeddable/public/tests/test_plugin.ts @@ -19,7 +19,8 @@ import { import { Query } from '@kbn/es-query'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; -import { EmbeddablePublicPlugin, EmbeddableSetup, EmbeddableStart } from '../plugin'; +import { EmbeddablePublicPlugin } from '../plugin'; +import type { EmbeddableSetup, EmbeddableStart } from '../types'; export interface TestPluginReturn { plugin: EmbeddablePublicPlugin; coreSetup: CoreSetup; diff --git a/src/plugins/embeddable/public/types.ts b/src/plugins/embeddable/public/types.ts index 72d8052706254..2d97adfc2b8e0 100644 --- a/src/plugins/embeddable/public/types.ts +++ b/src/plugins/embeddable/public/types.ts @@ -7,36 +7,65 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { SerializableRecord } from '@kbn/utility-types'; -import { SavedObjectAttributes } from '@kbn/core/public'; -import type { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common'; -import { PersistableState, PersistableStateDefinition } from '@kbn/kibana-utils-plugin/common'; -import { - EmbeddableFactory, - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, - EmbeddableFactoryDefinition, -} from './lib/embeddables'; +import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { Start as InspectorStart } from '@kbn/inspector-plugin/public'; +import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import type { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { PersistableStateService } from '@kbn/kibana-utils-plugin/common'; +import type { registerAddFromLibraryType } from './add_from_library/registry'; +import type { registerReactEmbeddableFactory } from './react_embeddable_system'; +import type { EmbeddableStateTransfer } from './lib'; +import type { EmbeddableStateWithType } from '../common'; +import { EnhancementRegistryDefinition } from './enhancements/types'; -export type EmbeddableFactoryRegistry = Map; -export type EnhancementsRegistry = Map; +export interface EmbeddableSetupDependencies { + uiActions: UiActionsSetup; +} -export interface EnhancementRegistryDefinition

- extends PersistableStateDefinition

{ - id: string; +export interface EmbeddableStartDependencies { + uiActions: UiActionsStart; + inspector: InspectorStart; + usageCollection: UsageCollectionStart; + contentManagement: ContentManagementPublicStart; + savedObjectsManagement: SavedObjectsManagementPluginStart; + savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; } -export interface EnhancementRegistryItem

- extends PersistableState

{ - id: string; +export interface EmbeddableSetup { + /** + * Register a saved object type with the "Add from library" flyout. + * + * @example + * registerAddFromLibraryType({ + * onAdd: (container, savedObject) => { + * container.addNewPanel({ + * panelType: CONTENT_ID, + * initialState: savedObject.attributes, + * }); + * }, + * savedObjectType: MAP_SAVED_OBJECT_TYPE, + * savedObjectName: i18n.translate('xpack.maps.mapSavedObjectLabel', { + * defaultMessage: 'Map', + * }), + * getIconForSavedObject: () => APP_ICON, + * }); + */ + registerAddFromLibraryType: typeof registerAddFromLibraryType; + + /** + * Registers an async {@link ReactEmbeddableFactory} getter. + */ + registerReactEmbeddableFactory: typeof registerReactEmbeddableFactory; + + /** + * @deprecated + */ + registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; } -export type EmbeddableFactoryProvider = < - I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput, - E extends IEmbeddable = IEmbeddable, - T extends FinderAttributes = SavedObjectAttributes ->( - def: EmbeddableFactoryDefinition -) => EmbeddableFactory; +export interface EmbeddableStart extends PersistableStateService { + getStateTransfer: (storage?: Storage) => EmbeddableStateTransfer; +} diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 731044f9f61ca..b794f947c0fa1 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -2774,7 +2774,6 @@ "embeddableApi.common.constants.grouping.other": "Autre", "embeddableApi.contextMenuTrigger.description": "Une nouvelle action sera ajoutée au menu contextuel du panneau", "embeddableApi.contextMenuTrigger.title": "Menu contextuel", - "embeddableApi.errors.embeddableFactoryNotFound": "Impossible de charger {type}. Veuillez effectuer une mise à niveau vers la distribution par défaut d'Elasticsearch et de Kibana avec la licence appropriée.", "embeddableApi.errors.paneldoesNotExist": "Panneau introuvable", "embeddableApi.errors.panelIncompatibleError": "L'API du panneau n'est pas compatible", "embeddableApi.multiValueClickTrigger.description": "Sélection de plusieurs valeurs d'une même dimension dans la visualisation", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index e262a4bb35a41..eda56781402ff 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -2769,7 +2769,6 @@ "embeddableApi.common.constants.grouping.other": "Other", "embeddableApi.contextMenuTrigger.description": "新しいアクションがパネルのコンテキストメニューに追加されます", "embeddableApi.contextMenuTrigger.title": "コンテキストメニュー", - "embeddableApi.errors.embeddableFactoryNotFound": "{type} を読み込めません。Elasticsearch と Kibanaのデフォルトのディストリビューションを適切なライセンスでアップグレードしてください。", "embeddableApi.errors.paneldoesNotExist": "パネルが見つかりません", "embeddableApi.errors.panelIncompatibleError": "パネルAPIに互換性がありません", "embeddableApi.multiValueClickTrigger.description": "ビジュアライゼーションの1つのディメンションの複数値を選択しています", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index f63f6602f7257..db29389b8a0eb 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -2761,7 +2761,6 @@ "embeddableApi.common.constants.grouping.other": "其他", "embeddableApi.contextMenuTrigger.description": "会将一个新操作添加到该面板的上下文菜单", "embeddableApi.contextMenuTrigger.title": "上下文菜单", - "embeddableApi.errors.embeddableFactoryNotFound": "{type} 无法加载。请升级到具有适当许可的默认 Elasticsearch 和 Kibana 分发。", "embeddableApi.errors.paneldoesNotExist": "未找到面板", "embeddableApi.errors.panelIncompatibleError": "面板 API 不兼容", "embeddableApi.multiValueClickTrigger.description": "在可视化上选择多个单一维度的值", diff --git a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts b/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts index 96e77a54e5398..7cadd254ca3ed 100644 --- a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts +++ b/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts @@ -5,11 +5,6 @@ * 2.0. */ -import { type ExplicitInputWithAttributes } from '@kbn/embeddable-plugin/public/lib'; -import { EmbeddableInput } from '../../types'; - -export const encode = ( - input: ExplicitInputWithAttributes | Partial | Readonly -) => Buffer.from(JSON.stringify(input)).toString('base64'); +export const encode = (input: object) => Buffer.from(JSON.stringify(input)).toString('base64'); export const decode = (serializedInput: string) => JSON.parse(Buffer.from(serializedInput, 'base64').toString()); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx index dc638adb979ed..22eb31d1a9086 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx @@ -8,54 +8,9 @@ import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import React from 'react'; -import { EmbeddableFactoryDefinition, IEmbeddable } from '@kbn/embeddable-plugin/public'; import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; import { EditorMenu } from '../editor_menu.component'; -const testFactories: EmbeddableFactoryDefinition[] = [ - { - type: 'ml_anomaly_swimlane', - getDisplayName: () => 'Anomaly swimlane', - getIconType: () => '', - getDescription: () => 'Description for anomaly swimlane', - isEditable: () => Promise.resolve(true), - latestVersion: '1.0.0', - create: () => Promise.resolve({ id: 'swimlane_embeddable' } as IEmbeddable), - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'ml_anomaly_chart', - getDisplayName: () => 'Anomaly chart', - getIconType: () => '', - getDescription: () => 'Description for anomaly chart', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - latestVersion: '1.0.0', - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'log_stream', - getDisplayName: () => 'Log stream', - getIconType: () => '', - getDescription: () => 'Description for log stream', - latestVersion: '1.0.0', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - }, -]; - const testVisTypes: BaseVisType[] = [ { title: 'TSVB', icon: '', description: 'Description of TSVB', name: 'tsvb' } as BaseVisType, { @@ -95,11 +50,9 @@ const testVisTypeAliases: VisTypeAlias[] = [ storiesOf('components/WorkpadHeader/EditorMenu', module).add('default', () => ( action('createNewVisType')} - createNewEmbeddableFromFactory={() => action('createNewEmbeddableFromFactory')} createNewEmbeddableFromAction={() => action('createNewEmbeddableFromAction')} /> )); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx index 5c424961d7f50..188898798dd7a 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx @@ -7,12 +7,7 @@ import React, { FC, useCallback } from 'react'; -import { - EuiContextMenu, - EuiContextMenuItemIcon, - EuiContextMenuPanelItemDescriptor, -} from '@elastic/eui'; -import { EmbeddableFactoryDefinition } from '@kbn/embeddable-plugin/public'; +import { EuiContextMenu, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; import { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public/actions'; @@ -28,21 +23,11 @@ const strings = { }), }; -interface FactoryGroup { - id: string; - appName: string; - icon: EuiContextMenuItemIcon; - panelId: number; - factories: EmbeddableFactoryDefinition[]; -} - interface Props { - factories: EmbeddableFactoryDefinition[]; addPanelActions: Action[]; promotedVisTypes: BaseVisType[]; visTypeAliases: VisTypeAlias[]; createNewVisType: (visType?: BaseVisType | VisTypeAlias) => () => void; - createNewEmbeddableFromFactory: (factory: EmbeddableFactoryDefinition) => () => void; createNewEmbeddableFromAction: ( action: Action, context: ActionExecutionContext, @@ -51,46 +36,14 @@ interface Props { } export const EditorMenu: FC = ({ - factories, addPanelActions, promotedVisTypes, visTypeAliases, createNewVisType, createNewEmbeddableFromAction, - createNewEmbeddableFromFactory, }: Props) => { - const factoryGroupMap: Record = {}; - const ungroupedFactories: EmbeddableFactoryDefinition[] = []; const canvasApi = useCanvasApi(); - let panelCount = 1; - - // Maps factories with a group to create nested context menus for each group type - // and pushes ungrouped factories into a separate array - factories.forEach((factory: EmbeddableFactoryDefinition, index) => { - const { grouping } = factory; - - if (grouping) { - grouping.forEach((group) => { - if (factoryGroupMap[group.id]) { - factoryGroupMap[group.id].factories.push(factory); - } else { - factoryGroupMap[group.id] = { - id: group.id, - appName: group.getDisplayName ? group.getDisplayName({}) : group.id, - icon: (group.getIconType ? group.getIconType({}) : 'empty') as EuiContextMenuItemIcon, - factories: [factory], - panelId: panelCount, - }; - - panelCount++; - } - }); - } else { - ungroupedFactories.push(factory); - } - }); - const getVisTypeMenuItem = (visType: BaseVisType): EuiContextMenuPanelItemDescriptor => { const { name, title, titleInWizard, description, icon = 'empty' } = visType; return { @@ -116,22 +69,6 @@ export const EditorMenu: FC = ({ }; }; - const getEmbeddableFactoryMenuItem = ( - factory: EmbeddableFactoryDefinition - ): EuiContextMenuPanelItemDescriptor => { - const icon = factory?.getIconType ? factory.getIconType() : 'empty'; - - const toolTipContent = factory?.getDescription ? factory.getDescription() : undefined; - - return { - name: factory.getDisplayName(), - icon, - toolTipContent, - onClick: createNewEmbeddableFromFactory(factory), - 'data-test-subj': `createNew-${factory.type}`, - }; - }; - const getAddPanelActionMenuItems = useCallback( (closePopover: () => void) => { return addPanelActions.map((item) => { @@ -158,23 +95,9 @@ export const EditorMenu: FC = ({ items: [ ...visTypeAliases.map(getVisTypeAliasMenuItem), ...getAddPanelActionMenuItems(closePopover), - ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), ...promotedVisTypes.map(getVisTypeMenuItem), - ...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({ - name: appName, - icon, - panel: panelId, - 'data-test-subj': `canvasEditorMenu-${id}Group`, - })), ], }, - ...Object.values(factoryGroupMap).map( - ({ appName, panelId, factories: groupFactories }: FactoryGroup) => ({ - id: panelId, - title: appName, - items: groupFactories.map(getEmbeddableFactoryMenuItem), - }) - ), ]; return ( diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx index 06d20e919dcbe..60933dd4d121b 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { FC, useCallback, useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { VisGroups, @@ -13,19 +13,12 @@ import { type VisTypeAlias, type VisParams, } from '@kbn/visualizations-plugin/public'; -import { - EmbeddableFactory, - EmbeddableFactoryDefinition, - EmbeddableInput, -} from '@kbn/embeddable-plugin/public'; import { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public/actions'; import { trackCanvasUiMetric, METRIC_TYPE } from '../../../lib/ui_metric'; import { CANVAS_APP } from '../../../../common/lib'; import { ElementSpec } from '../../../../types'; import { EditorMenu as Component } from './editor_menu.component'; -import { embeddableInputToExpression } from '../../../../canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression'; -import { EmbeddableInput as CanvasEmbeddableInput } from '../../../../canvas_plugin_src/expression_types'; import { useCanvasApi } from '../../hooks/use_canvas_api'; import { ADD_CANVAS_ELEMENT_TRIGGER } from '../../../state/triggers/add_canvas_element_trigger'; import { @@ -41,11 +34,6 @@ interface Props { addElement: (element: Partial) => void; } -interface UnwrappedEmbeddableFactory { - factory: EmbeddableFactory; - isEditable: boolean; -} - export const EditorMenu: FC = ({ addElement }) => { const { pathname, search, hash } = useLocation(); const stateTransferService = embeddableService.getStateTransfer(); @@ -53,26 +41,6 @@ export const EditorMenu: FC = ({ addElement }) => { const [addPanelActions, setAddPanelActions] = useState>>([]); - const embeddableFactories = useMemo( - () => (embeddableService ? Array.from(embeddableService.getEmbeddableFactories()) : []), - [] - ); - - const [unwrappedEmbeddableFactories, setUnwrappedEmbeddableFactories] = useState< - UnwrappedEmbeddableFactory[] - >([]); - - useEffect(() => { - Promise.all( - embeddableFactories.map>(async (factory) => ({ - factory, - isEditable: await factory.isEditable(), - })) - ).then((factories) => { - setUnwrappedEmbeddableFactories(factories); - }); - }, [embeddableFactories]); - useEffect(() => { let mounted = true; async function loadPanelActions() { @@ -123,33 +91,6 @@ export const EditorMenu: FC = ({ addElement }) => { [stateTransferService, pathname, search, hash] ); - const createNewEmbeddableFromFactory = useCallback( - (factory: EmbeddableFactoryDefinition) => async () => { - if (trackCanvasUiMetric) { - trackCanvasUiMetric(METRIC_TYPE.CLICK, factory.type); - } - - let embeddableInput; - if (factory.getExplicitInput) { - embeddableInput = await factory.getExplicitInput(); - } else { - const newEmbeddable = await factory.create({} as EmbeddableInput); - embeddableInput = newEmbeddable?.getInput(); - } - - if (embeddableInput) { - const expression = embeddableInputToExpression( - embeddableInput as CanvasEmbeddableInput, - factory.type, - undefined, - true - ); - addElement({ expression }); - } - }, - [addElement] - ); - const createNewEmbeddableFromAction = useCallback( (action: Action, context: ActionExecutionContext, closePopover: () => void) => (event: React.MouseEvent) => { @@ -190,31 +131,17 @@ export const EditorMenu: FC = ({ addElement }) => { ) .filter(({ disableCreate }: VisTypeAlias) => !disableCreate); - const factories = unwrappedEmbeddableFactories - .filter( - ({ isEditable, factory: { type, canCreateNew, isContainerType } }) => - isEditable && - !isContainerType && - canCreateNew() && - !['visualization', 'ml', 'links'].some((factoryType) => { - return type.includes(factoryType); - }) - ) - .map(({ factory }) => factory); - const promotedVisTypes = getVisTypesByGroup(VisGroups.PROMOTED); const legacyVisTypes = getVisTypesByGroup(VisGroups.LEGACY); return ( >).concat( promotedVisTypes, legacyVisTypes )} - factories={factories} addPanelActions={addPanelActions} visTypeAliases={visTypeAliases} /> diff --git a/x-pack/plugins/canvas/public/services/kibana_services.ts b/x-pack/plugins/canvas/public/services/kibana_services.ts index 92980d712fb4f..b820ca1396ffc 100644 --- a/x-pack/plugins/canvas/public/services/kibana_services.ts +++ b/x-pack/plugins/canvas/public/services/kibana_services.ts @@ -11,7 +11,7 @@ import type { ContentManagementPublicStart } from '@kbn/content-management-plugi import type { CoreStart, PluginInitializerContext } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import type { EmbeddableStart } from '@kbn/embeddable-plugin/public/plugin'; +import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { ReportingStart } from '@kbn/reporting-plugin/public'; diff --git a/x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/profiling_embeddable.tsx b/x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/profiling_embeddable.tsx deleted file mode 100644 index 989b446ca3b21..0000000000000 --- a/x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/profiling_embeddable.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { css } from '@emotion/react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import React, { useEffect, useRef, useState } from 'react'; -import { ObservabilitySharedStart } from '../../../plugin'; - -export function ProfilingEmbeddable({ - embeddableFactoryId, - height, - ...props -}: T & { embeddableFactoryId: string; height?: string }) { - const { embeddable: embeddablePlugin } = useKibana().services; - const [embeddable, setEmbeddable] = useState(); - const embeddableRoot: React.RefObject = useRef(null); - - useEffect(() => { - async function createEmbeddable() { - const factory = embeddablePlugin?.getEmbeddableFactory(embeddableFactoryId); - const input = { ...props, id: 'embeddable_profiling' }; - const embeddableObject = await factory?.create(input); - setEmbeddable(embeddableObject); - } - createEmbeddable(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - if (embeddableRoot.current && embeddable) { - embeddable.render(embeddableRoot.current); - } - }, [embeddable, embeddableRoot]); - - useEffect(() => { - if (embeddable) { - embeddable.updateInput(props); - embeddable.reload(); - } - }, [embeddable, props]); - - return ( -
- ); -} diff --git a/x-pack/solutions/observability/plugins/ux/public/application/application.test.tsx b/x-pack/solutions/observability/plugins/ux/public/application/application.test.tsx index 2b9dd676eac17..5414ed2963ed5 100644 --- a/x-pack/solutions/observability/plugins/ux/public/application/application.test.tsx +++ b/x-pack/solutions/observability/plugins/ux/public/application/application.test.tsx @@ -51,18 +51,8 @@ const mockPlugin = { observabilityAIAssistant: mockAIAssistantPlugin, }; -const mockEmbeddable = embeddablePluginMock.createStartContract(); - -mockEmbeddable.getEmbeddableFactory = jest.fn().mockImplementation(() => ({ - create: () => ({ - reload: jest.fn(), - setRenderTooltipContent: jest.fn(), - setLayerList: jest.fn(), - }), -})); - const mockCorePlugins = { - embeddable: mockEmbeddable, + embeddable: embeddablePluginMock.createStartContract(), inspector: {}, maps: {}, observabilityShared: {