[embeddable] remove setCustomEmbeddableFactoryProvider from setup API (#203853)

Part of https://github.com/elastic/kibana/issues/167429

Remove `setCustomEmbeddableFactoryProvider` from embeddable setup API.
`setCustomEmbeddableFactoryProvider` only used in `embeddable_enhanced`
plugin. Replaced with `initializeReactEmbeddableDynamicActions` in react
embeddable system.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2024-12-12 09:11:48 -07:00 committed by GitHub
parent b059879764
commit 7218d01aa4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 4 additions and 285 deletions

View file

@ -99,7 +99,6 @@ const createSetupContract = (): Setup => {
registerReactEmbeddableFactory: jest.fn().mockImplementation(registerReactEmbeddableFactory),
registerEmbeddableFactory: jest.fn(),
registerEnhancement: jest.fn(),
setCustomEmbeddableFactoryProvider: jest.fn(),
};
return setupContract;
};

View file

@ -9,83 +9,6 @@
import { coreMock } from '@kbn/core/public/mocks';
import { testPlugin } from './tests/test_plugin';
import { EmbeddableFactoryProvider } from './types';
import { defaultEmbeddableFactoryProvider } from './lib';
import { HelloWorldEmbeddable } from './tests/fixtures';
test('can set custom embeddable factory provider', async () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const { setup, doStart } = testPlugin(coreSetup, coreStart);
const customProvider: EmbeddableFactoryProvider = (def) => ({
...defaultEmbeddableFactoryProvider(def),
getDisplayName: () => 'Intercepted!',
});
setup.setCustomEmbeddableFactoryProvider(customProvider);
setup.registerEmbeddableFactory('test', {
type: 'test',
latestVersion: '1.0.0',
create: () => Promise.resolve(undefined),
getDisplayName: () => 'Test',
isEditable: () => Promise.resolve(true),
});
const start = doStart();
const factory = start.getEmbeddableFactory('test');
expect(factory!.getDisplayName()).toEqual('Intercepted!');
});
test('custom embeddable factory provider test for intercepting embeddable creation and destruction', async () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const { setup, doStart } = testPlugin(coreSetup, coreStart);
let updateCount = 0;
const customProvider: EmbeddableFactoryProvider = (def) => {
return {
...defaultEmbeddableFactoryProvider(def),
create: async (input, parent) => {
const embeddable = await defaultEmbeddableFactoryProvider(def).create(input, parent);
if (embeddable) {
const subscription = embeddable.getInput$().subscribe(
() => {
updateCount++;
},
() => {},
() => {
subscription.unsubscribe();
updateCount = 0;
}
);
}
return embeddable;
},
};
};
setup.setCustomEmbeddableFactoryProvider(customProvider);
setup.registerEmbeddableFactory('test', {
type: 'test',
latestVersion: '1.0.0',
create: (input, parent) => Promise.resolve(new HelloWorldEmbeddable(input, parent)),
getDisplayName: () => 'Test',
isEditable: () => Promise.resolve(true),
});
const start = doStart();
const factory = start.getEmbeddableFactory('test');
const embeddable = await factory?.create({ id: '123' });
embeddable!.updateInput({ title: 'boo' });
// initial subscription, plus the second update.
expect(updateCount).toEqual(2);
embeddable!.destroy();
await new Promise((resolve) => process.nextTick(resolve));
expect(updateCount).toEqual(0);
});
describe('embeddable factory', () => {
const coreSetup = coreMock.createSetup();

View file

@ -27,7 +27,6 @@ import type { ContentManagementPublicStart } from '@kbn/content-management-plugi
import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public';
import {
EmbeddableFactoryRegistry,
EmbeddableFactoryProvider,
EnhancementsRegistry,
EnhancementRegistryDefinition,
EnhancementRegistryItem,
@ -108,10 +107,6 @@ export interface EmbeddableSetup {
* @deprecated
*/
registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void;
/**
* @deprecated
*/
setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void;
}
export interface EmbeddableStart extends PersistableStateService<EmbeddableStateWithType> {
@ -137,7 +132,6 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
new Map();
private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map();
private readonly enhancements: EnhancementsRegistry = new Map();
private customEmbeddableFactoryProvider?: EmbeddableFactoryProvider;
private stateTransferService: EmbeddableStateTransfer = {} as EmbeddableStateTransfer;
private isRegistryReady = false;
private appList?: ReadonlyMap<string, PublicAppInfo>;
@ -154,25 +148,12 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
registerEmbeddableFactory: this.registerEmbeddableFactory,
registerEnhancement: this.registerEnhancement,
setCustomEmbeddableFactoryProvider: (provider: EmbeddableFactoryProvider) => {
if (this.customEmbeddableFactoryProvider) {
throw new Error(
'Custom embeddable factory provider is already set, and can only be set once'
);
}
this.customEmbeddableFactoryProvider = provider;
},
};
}
public start(core: CoreStart, deps: EmbeddableStartDependencies): EmbeddableStart {
this.embeddableFactoryDefinitions.forEach((def) => {
this.embeddableFactories.set(
def.type,
this.customEmbeddableFactoryProvider
? this.customEmbeddableFactoryProvider(def)
: defaultEmbeddableFactoryProvider(def)
);
this.embeddableFactories.set(def.type, defaultEmbeddableFactoryProvider(def));
});
this.appListSubscription = core.application.applications$.subscribe((appList) => {
@ -311,12 +292,7 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
if (!this.embeddableFactories.get(type)) {
const def = this.embeddableFactoryDefinitions.get(type);
if (!def) return;
this.embeddableFactories.set(
type,
this.customEmbeddableFactoryProvider
? this.customEmbeddableFactoryProvider(def)
: defaultEmbeddableFactoryProvider(def)
);
this.embeddableFactories.set(type, defaultEmbeddableFactoryProvider(def));
}
};
}

View file

@ -15942,7 +15942,6 @@
"xpack.elasticAssistantPlugin.attackDiscovery.defaultAttackDiscoveryGraph.nodes.retriever.helpers.throwIfErrorCountsExceeded.maxGenerationAttemptsErrorMessage": "Nombre maximum de tentatives de génération ({generationAttempts}) atteint. Essayez d'envoyer un nombre d'alertes moins élevé à ce modèle.",
"xpack.elasticAssistantPlugin.attackDiscovery.defaultAttackDiscoveryGraph.nodes.retriever.helpers.throwIfErrorCountsExceeded.maxHallucinationFailuresErrorMessage": "Nombre maximum d'échecs d'hallucinations ({hallucinationFailures}) atteint. Essayez d'envoyer un nombre d'alertes moins élevé à ce modèle.",
"xpack.elasticAssistantPlugin.server.newChat": "Nouveau chat",
"xpack.embeddableEnhanced.Drilldowns": "Explorations",
"xpack.enterpriseSearch.accessControlIndexSelector.p.accessControlSyncsAreLabel": "Les synchronisations de contrôle d'accès maintiennent les informations d'autorisation à jour pour assurer la sécurité au niveau du document (DLS)",
"xpack.enterpriseSearch.actions.backButtonLabel": "Retour",
"xpack.enterpriseSearch.actions.cancelButtonLabel": "Annuler",

View file

@ -15805,7 +15805,6 @@
"xpack.elasticAssistantPlugin.attackDiscovery.defaultAttackDiscoveryGraph.nodes.retriever.helpers.throwIfErrorCountsExceeded.maxGenerationAttemptsErrorMessage": "最大生成試行回数({generationAttempts})に達しました。このモデルに送信するアラートの数を減らしてください。",
"xpack.elasticAssistantPlugin.attackDiscovery.defaultAttackDiscoveryGraph.nodes.retriever.helpers.throwIfErrorCountsExceeded.maxHallucinationFailuresErrorMessage": "最大ハルシネーション失敗回数({hallucinationFailures})に達しました。このモデルに送信するアラートの数を減らしてください。",
"xpack.elasticAssistantPlugin.server.newChat": "新しいチャット",
"xpack.embeddableEnhanced.Drilldowns": "ドリルダウン",
"xpack.enterpriseSearch.accessControlIndexSelector.p.accessControlSyncsAreLabel": "アクセス制御の同期により、ドキュメントレベルセキュリティDLSの権限情報が最新の状態に保たれます。",
"xpack.enterpriseSearch.actions.backButtonLabel": "戻る",
"xpack.enterpriseSearch.actions.cancelButtonLabel": "キャンセル",

View file

@ -15525,7 +15525,6 @@
"xpack.elasticAssistantPlugin.attackDiscovery.defaultAttackDiscoveryGraph.nodes.retriever.helpers.throwIfErrorCountsExceeded.maxGenerationAttemptsErrorMessage": "已达到最大生成尝试次数 ({generationAttempts})。尝试向此模型发送更少的告警。",
"xpack.elasticAssistantPlugin.attackDiscovery.defaultAttackDiscoveryGraph.nodes.retriever.helpers.throwIfErrorCountsExceeded.maxHallucinationFailuresErrorMessage": "已达到最大幻觉失败次数 ({hallucinationFailures})。尝试向此模型发送更少的告警。",
"xpack.elasticAssistantPlugin.server.newChat": "新聊天",
"xpack.embeddableEnhanced.Drilldowns": "向下钻取",
"xpack.enterpriseSearch.accessControlIndexSelector.p.accessControlSyncsAreLabel": "访问控制同步会使权限信息保持最新以实现文档级别安全性 (DLS)",
"xpack.enterpriseSearch.actions.backButtonLabel": "返回",
"xpack.enterpriseSearch.actions.cancelButtonLabel": "取消",

View file

@ -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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { UiActionsPresentableGrouping as PresentableGrouping } from '@kbn/ui-actions-plugin/public';
export const drilldownGrouping: PresentableGrouping = [
{
id: 'drilldowns',
getDisplayName: () =>
i18n.translate('xpack.embeddableEnhanced.Drilldowns', {
defaultMessage: 'Drilldowns',
}),
getIconType: () => 'symlink',
order: 25,
},
];

View file

@ -1,8 +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.
*/
export * from './drilldown_grouping';

View file

@ -19,9 +19,7 @@ export function plugin(context: PluginInitializerContext) {
return new EmbeddableEnhancedPlugin(context);
}
export type { EnhancedEmbeddable, EnhancedEmbeddableContext } from './types';
export {
type HasDynamicActions,
apiHasDynamicActions,
} from './embeddables/interfaces/has_dynamic_actions';
export { drilldownGrouping as embeddableEnhancedDrilldownGrouping } from './actions';

View file

@ -6,23 +6,12 @@
*/
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public';
import {
defaultEmbeddableFactoryProvider,
EmbeddableContext,
EmbeddableFactory,
EmbeddableFactoryDefinition,
EmbeddableInput,
EmbeddableOutput,
EmbeddableSetup,
EmbeddableStart,
IEmbeddable,
} from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import {
apiHasUniqueId,
EmbeddableApiContext,
StateComparators,
} from '@kbn/presentation-publishing';
import type { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common';
import {
AdvancedUiActionsSetup,
AdvancedUiActionsStart,
@ -30,13 +19,12 @@ import {
UiActionsEnhancedDynamicActionManager as DynamicActionManager,
} from '@kbn/ui-actions-enhanced-plugin/public';
import deepEqual from 'react-fast-compare';
import { BehaviorSubject, distinctUntilChanged } from 'rxjs';
import { BehaviorSubject } from 'rxjs';
import {
DynamicActionStorage,
type DynamicActionStorageApi,
} from './embeddables/dynamic_action_storage';
import { HasDynamicActions } from './embeddables/interfaces/has_dynamic_actions';
import { EnhancedEmbeddable } from './types';
import { getDynamicActionsState } from './get_dynamic_actions_state';
export interface SetupDependencies {
@ -79,8 +67,6 @@ export class EmbeddableEnhancedPlugin
private uiActions?: StartDependencies['uiActionsEnhanced'];
public setup(core: CoreSetup<StartDependencies>, plugins: SetupDependencies): SetupContract {
this.setCustomEmbeddableFactoryProvider(plugins);
return {};
}
@ -94,45 +80,6 @@ export class EmbeddableEnhancedPlugin
public stop() {}
private setCustomEmbeddableFactoryProvider(plugins: SetupDependencies) {
plugins.embeddable.setCustomEmbeddableFactoryProvider(
<
I extends EmbeddableInput = EmbeddableInput,
O extends EmbeddableOutput = EmbeddableOutput,
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
T extends FinderAttributes = {}
>(
def: EmbeddableFactoryDefinition<I, O, E, T>
): EmbeddableFactory<I, O, E, T> => {
const factory: EmbeddableFactory<I, O, E, T> = defaultEmbeddableFactoryProvider<I, O, E, T>(
def
);
return {
...factory,
create: async (...args) => {
const embeddable = await factory.create(...args);
if (!embeddable) return embeddable;
return this.enhanceEmbeddableWithDynamicActions(embeddable);
},
createFromSavedObject: async (...args) => {
const embeddable = await factory.createFromSavedObject(...args);
if (!embeddable) return embeddable;
return this.enhanceEmbeddableWithDynamicActions(embeddable);
},
};
}
);
}
private readonly isEmbeddableContext = (context: unknown): context is EmbeddableContext => {
if (!(context as EmbeddableContext)?.embeddable) {
// eslint-disable-next-line no-console
console.warn('For drilldowns to work action context should contain .embeddable field.');
return false;
}
return true;
};
private initializeDynamicActions(
uuid: string,
getTitle: () => string | undefined,
@ -183,77 +130,6 @@ export class EmbeddableEnhancedPlugin
};
}
/**
* TODO: Remove this entire enhanceEmbeddableWithDynamicActions method once the embeddable refactor work is complete
*/
private enhanceEmbeddableWithDynamicActions<E extends IEmbeddable>(
embeddable: E
): EnhancedEmbeddable<E> {
const enhancedEmbeddable = embeddable as EnhancedEmbeddable<E>;
const dynamicActionsState$ = new BehaviorSubject<DynamicActionsSerializedState['enhancements']>(
{
dynamicActions: { events: [] },
...(embeddable.getInput().enhancements ?? {}),
}
);
const api = {
dynamicActionsState$,
setDynamicActions: (newState: DynamicActionsSerializedState['enhancements']) => {
embeddable.updateInput({ enhancements: newState });
},
};
/**
* Keep the dynamicActionsState$ publishing subject in sync with changes to the embeddable's input.
*/
embeddable
.getInput$()
.pipe(
distinctUntilChanged(({ enhancements: old }, { enhancements: updated }) =>
deepEqual(old, updated)
)
)
.subscribe((input) => {
dynamicActionsState$.next({
dynamicActions: { events: [] },
...(input.enhancements ?? {}),
} as DynamicActionsSerializedState['enhancements']);
});
const storage = new DynamicActionStorage(
String(embeddable.runtimeId),
embeddable.getTitle,
api
);
const dynamicActions = new DynamicActionManager({
isCompatible: async (context: unknown) => {
if (!this.isEmbeddableContext(context)) return false;
return context.embeddable.runtimeId === embeddable.runtimeId;
},
storage,
uiActions: this.uiActions!,
});
const stop = this.startDynamicActions(dynamicActions);
embeddable.getInput$().subscribe({
next: () => {
storage.reload$.next();
},
error: stop,
complete: stop,
});
enhancedEmbeddable.enhancements = {
...enhancedEmbeddable.enhancements,
dynamicActions,
};
enhancedEmbeddable.dynamicActionsState$ = api.dynamicActionsState$;
enhancedEmbeddable.setDynamicActions = api.setDynamicActions;
return enhancedEmbeddable;
}
private startDynamicActions(dynamicActions: DynamicActionManager) {
dynamicActions.start().catch((error) => {
/* eslint-disable no-console */

View file

@ -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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IEmbeddable } from '@kbn/embeddable-plugin/public';
import { HasDynamicActions } from './embeddables/interfaces/has_dynamic_actions';
export type EnhancedEmbeddable<E extends IEmbeddable = IEmbeddable> = E & HasDynamicActions;
/**
* @deprecated use `EmbeddableApiContext` from `@kbn/presentation-publishing`
*/
export interface EnhancedEmbeddableContext {
embeddable: EnhancedEmbeddable;
}

View file

@ -9,12 +9,9 @@
"kbn_references": [
"@kbn/core",
"@kbn/embeddable-plugin",
"@kbn/ui-actions-plugin",
"@kbn/ui-actions-enhanced-plugin",
"@kbn/i18n",
"@kbn/kibana-utils-plugin",
"@kbn/data-plugin",
"@kbn/saved-objects-finder-plugin",
"@kbn/presentation-publishing",
],
"exclude": [