Skip to content

Commit

Permalink
[embeddable] remove setCustomEmbeddableFactoryProvider from setup API (
Browse files Browse the repository at this point in the history
…elastic#203853)

Part of elastic#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 <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
3 people authored Dec 12, 2024
1 parent b059879 commit 7218d01
Show file tree
Hide file tree
Showing 12 changed files with 4 additions and 285 deletions.
1 change: 0 additions & 1 deletion src/plugins/embeddable/public/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ const createSetupContract = (): Setup => {
registerReactEmbeddableFactory: jest.fn().mockImplementation(registerReactEmbeddableFactory),
registerEmbeddableFactory: jest.fn(),
registerEnhancement: jest.fn(),
setCustomEmbeddableFactoryProvider: jest.fn(),
};
return setupContract;
};
Expand Down
77 changes: 0 additions & 77 deletions src/plugins/embeddable/public/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
28 changes: 2 additions & 26 deletions src/plugins/embeddable/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -108,10 +107,6 @@ export interface EmbeddableSetup {
* @deprecated
*/
registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void;
/**
* @deprecated
*/
setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void;
}

export interface EmbeddableStart extends PersistableStateService<EmbeddableStateWithType> {
Expand All @@ -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>;
Expand All @@ -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) => {
Expand Down Expand Up @@ -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));
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": "キャンセル",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": "取消",
Expand Down

This file was deleted.

8 changes: 0 additions & 8 deletions x-pack/plugins/embeddable_enhanced/public/actions/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions x-pack/plugins/embeddable_enhanced/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
128 changes: 2 additions & 126 deletions x-pack/plugins/embeddable_enhanced/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,25 @@
*/

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,
DynamicActionsState,
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 {
Expand Down Expand Up @@ -79,8 +67,6 @@ export class EmbeddableEnhancedPlugin
private uiActions?: StartDependencies['uiActionsEnhanced'];

public setup(core: CoreSetup<StartDependencies>, plugins: SetupDependencies): SetupContract {
this.setCustomEmbeddableFactoryProvider(plugins);

return {};
}

Expand All @@ -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,
Expand Down Expand Up @@ -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 */
Expand Down
Loading

0 comments on commit 7218d01

Please sign in to comment.