Skip to content

Commit

Permalink
[Embeddables Rebuild] Migrate Visualize (elastic#183197)
Browse files Browse the repository at this point in the history
## Summary

Closes elastic#174958

This migrates the Visualize embeddable to the new React Embeddable
framework.

Migrated:
- Edit visualization action
- Convert to lens action
- Extracting/injecting references on serialize and deserialize
- Inspector adapters
- Dashboard settings
- Drilldown support
- Timeslice/time slider support
- Custom time ranges

Also adds deprecation statements to legacy Embeddable factories

**In a second PR, we'll move the `embeddable` folder to
`legacy/embeddable` and rename `react_embeddable` to `embeddable`. I
don't know if git will be able to diff that change in a comprehensible
way in this PR, so I want to save it for the next one.**

### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Devon Thomson <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: Marta Bondyra <[email protected]>
  • Loading branch information
5 people authored Aug 30, 2024
1 parent 829c6ad commit ee28e20
Show file tree
Hide file tree
Showing 42 changed files with 1,605 additions and 126 deletions.
13 changes: 7 additions & 6 deletions packages/presentation/presentation_containers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,24 @@ export {
type CanDuplicatePanels,
type CanExpandPanels,
} from './interfaces/panel_management';
export {
canTrackContentfulRender,
type TrackContentfulRender,
type TracksQueryPerformance,
} from './interfaces/performance_trackers';
export {
apiIsPresentationContainer,
combineCompatibleChildrenApis,
getContainerParentFromAPI,
listenForCompatibleApi,
combineCompatibleChildrenApis,
type PanelPackage,
type PresentationContainer,
} from './interfaces/presentation_container';
export { apiPublishesSettings, type PublishesSettings } from './interfaces/publishes_settings';
export {
apiHasSerializableState,
type HasSerializableState,
type HasSnapshottableState,
type SerializedPanelState,
} from './interfaces/serialized_state';
export { tracksOverlays, type TracksOverlays } from './interfaces/tracks_overlays';
export {
canTrackContentfulRender,
type TrackContentfulRender,
type TracksQueryPerformance,
} from './interfaces/performance_trackers';
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"@kbn/es-query",
"@kbn/data-views-plugin",
"@kbn/expressions-plugin",
"@kbn/core-execution-context-common",
"@kbn/core-execution-context-common"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export type {
} from './latest';

export * as VisualizationV1 from './v1';
export type { Reference } from './v1';
4 changes: 3 additions & 1 deletion src/plugins/visualizations/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
BUCKET_TYPES,
} from '@kbn/data-plugin/common';
import type { SerializedFieldFormat } from '@kbn/field-formats-plugin/common';
import { Reference } from './content_management';

export interface VisParams {
[key: string]: any;
Expand All @@ -36,8 +37,9 @@ export type {
export interface SerializedVisData {
expression?: string;
aggs: AggConfigSerialized[];
searchSource: SerializedSearchSourceFields;
searchSource: SerializedSearchSourceFields & { indexRefName?: string };
savedSearchId?: string;
savedSearchRefName?: string | Reference;
}

export interface SerializedVis<T = VisParams> {
Expand Down
12 changes: 4 additions & 8 deletions src/plugins/visualizations/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,18 @@
"savedObjectsFinder",
"savedObjectsManagement",
"savedSearch",
"contentManagement",
"contentManagement"
],
"optionalPlugins": [
"home",
"share",
"spaces",
"savedObjectsTaggingOss",
"serverless",
"noDataPage"
],
"requiredBundles": [
"kibanaUtils",
"kibanaReact",
"charts",
"savedObjects",
"noDataPage",
"embeddableEnhanced"
],
"requiredBundles": ["kibanaUtils", "kibanaReact", "charts", "savedObjects"],
"extraPublicDirs": [
"common/constants",
"common/utils",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import { urlFor } from '../utils/saved_visualize_utils';
import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
import { createVisualizeEmbeddableAsync } from './visualize_embeddable_async';

/** @deprecated
* VisualizeEmbeddable is no longer registered with the legacy embeddable system and is only
* used within the visualize editor.
*/
export const createVisEmbeddableFromObject =
(deps: VisualizeEmbeddableFactoryDeps) =>
async (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export type VisualizeSavedObjectAttributes = SavedObjectAttributes & {
export type VisualizeByValueInput = { attributes: VisualizeSavedObjectAttributes } & VisualizeInput;
export type VisualizeByReferenceInput = SavedObjectEmbeddableInput & VisualizeInput;

/** @deprecated
* VisualizeEmbeddable is no longer registered with the legacy embeddable system and is only
* used within the visualize editor.
*/
export class VisualizeEmbeddable
extends Embeddable<VisualizeInput, VisualizeOutput>
implements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

import type { VisualizeEmbeddable as VisualizeEmbeddableType } from './visualize_embeddable';

/** @deprecated
* VisualizeEmbeddable is no longer registered with the legacy embeddable system and is only
* used within the visualize editor.
*/
export const createVisualizeEmbeddableAsync = async (
...args: ConstructorParameters<typeof VisualizeEmbeddableType>
) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export interface VisualizeEmbeddableFactoryDeps {
>;
}

/** @deprecated
* VisualizeEmbeddable is no longer registered with the legacy embeddable system and is only
* used within the visualize editor.
*/
export class VisualizeEmbeddableFactory
implements
EmbeddableFactoryDefinition<
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/visualizations/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export function plugin(initializerContext: PluginInitializerContext) {
export { TypesService } from './vis_types/types_service';
export {
apiHasVisualizeConfig,
VISUALIZE_EMBEDDABLE_TYPE,
VIS_EVENT_TO_TRIGGER,
COMMON_VISUALIZATION_GROUPING,
} from './embeddable';
Expand All @@ -38,12 +37,13 @@ export type {
VisualizationClient,
SerializableAttributes,
} from './vis_types';
export type { VisualizeEditorInput } from './react_embeddable/types';
export type { Vis, SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract<VisualizeEmbeddableFactory>;
export type VisualizeEmbeddableContract = PublicContract<VisualizeEmbeddable>;
export type { VisualizeInput, VisualizeEmbeddable, HasVisualizeConfig } from './embeddable';
export type { SchemaConfig } from '../common/types';
export { updateOldState } from './legacy/vis_update_state';
export type { VisualizeInput, VisualizeEmbeddable, HasVisualizeConfig } from './embeddable';
export type { PersistedState } from './persisted_state';
export type {
ISavedVis,
Expand All @@ -63,6 +63,7 @@ export {
LegendSize,
LegendSizeToPixels,
DEFAULT_LEGEND_SIZE,
VISUALIZE_EMBEDDABLE_TYPE,
} from '../common/constants';
export type { SavedVisState, VisParams, Dimension } from '../common';
export { prepareLogTable, XYCurveTypes } from '../common';
Expand Down
1 change: 0 additions & 1 deletion src/plugins/visualizations/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ const createInstance = async () => {
application: applicationServiceMock.createStartContract(),
embeddable: embeddablePluginMock.createStartContract(),
spaces: spacesPluginMock.createStartContract(),
getAttributeService: jest.fn(),
savedObjectsClient: coreMock.createStart().savedObjects.client,
savedObjects: savedObjectsPluginMock.createStartContract(),
savedObjectsTaggingOss: savedObjectTaggingOssPluginMock.createStart(),
Expand Down
84 changes: 40 additions & 44 deletions src/plugins/visualizations/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import {
ContentManagementPublicStart,
} from '@kbn/content-management-plugin/public';
import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public';
import { EmbeddableEnhancedPluginStart } from '@kbn/embeddable-enhanced-plugin/public';

import type { TypesSetup, TypesStart } from './vis_types';
import type { VisualizeServices } from './visualize_app/types';
import {
Expand All @@ -83,11 +85,6 @@ import { xyDimension as xyDimensionExpressionFunction } from '../common/expressi
import { visDimension as visDimensionExpressionFunction } from '../common/expression_functions/vis_dimension';
import { range as rangeExpressionFunction } from '../common/expression_functions/range';
import { TypesService } from './vis_types/types_service';
import {
createVisEmbeddableFromObject,
VISUALIZE_EMBEDDABLE_TYPE,
VisualizeEmbeddableFactory,
} from './embeddable';
import {
setUISettings,
setTypes,
Expand Down Expand Up @@ -115,18 +112,20 @@ import {
setSavedObjectsManagement,
setContentManagement,
setSavedSearch,
setDataViews,
setInspector,
getTypes,
} from './services';
import { VisualizeConstants } from '../common/constants';
import { VisualizeConstants, VISUALIZE_EMBEDDABLE_TYPE } from '../common/constants';
import { EditInLensAction } from './actions/edit_in_lens_action';
import { ListingViewRegistry, SerializedVis } from './types';
import { ListingViewRegistry } from './types';
import {
LATEST_VERSION,
CONTENT_ID,
VisualizationSavedObjectAttributes,
} from '../common/content_management';
import { SerializedVisData } from '../common';
import { VisualizeByValueInput } from './embeddable/visualize_embeddable';
import { AddAggVisualizationPanelAction } from './actions/add_agg_vis_action';
import { VisualizeSerializedState } from './react_embeddable/types';

/**
* Interface for this plugin's returned setup/start contracts.
Expand Down Expand Up @@ -163,7 +162,6 @@ export interface VisualizationsStartDeps {
inspector: InspectorStart;
uiActions: UiActionsStart;
application: ApplicationStart;
getAttributeService: EmbeddableStart['getAttributeService'];
navigation: NavigationStart;
presentationUtil: PresentationUtilPluginStart;
savedObjects: SavedObjectsStart;
Expand All @@ -181,6 +179,7 @@ export interface VisualizationsStartDeps {
contentManagement: ContentManagementPublicStart;
serverless?: ServerlessPluginStart;
noDataPage?: NoDataPagePluginStart;
embeddableEnhanced?: EmbeddableEnhancedPluginStart;
}

/**
Expand Down Expand Up @@ -308,6 +307,7 @@ export class VisualizationsPlugin
* this should be replaced to use only scoped history after moving legacy apps to browser routing
*/
const history = createHashHistory();
const { createVisEmbeddableFromObject } = await import('./embeddable');
const services: VisualizeServices = {
...coreStart,
history,
Expand All @@ -332,6 +332,7 @@ export class VisualizationsPlugin
embeddable: pluginsStart.embeddable,
stateTransferService: pluginsStart.embeddable.getStateTransfer(),
setActiveUrl,
/** @deprecated */
createVisEmbeddableFromObject: createVisEmbeddableFromObject({ start }),
scopedHistory: params.history,
restorePreviousUrl,
Expand Down Expand Up @@ -400,8 +401,31 @@ export class VisualizationsPlugin
uiActions.registerTrigger(dashboardVisualizationPanelTrigger);
const editInLensAction = new EditInLensAction(data.query.timefilter.timefilter);
uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editInLensAction);
const embeddableFactory = new VisualizeEmbeddableFactory({ start });
embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
embeddable.registerReactEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, async () => {
const {
plugins: { embeddable: embeddableStart, embeddableEnhanced: embeddableEnhancedStart },
} = start();

const { getVisualizeEmbeddableFactory } = await import('./react_embeddable');
return getVisualizeEmbeddableFactory({ embeddableStart, embeddableEnhancedStart });
});
embeddable.registerReactEmbeddableSavedObject<VisualizationSavedObjectAttributes>({
onAdd: (container, savedObject) => {
container.addNewPanel<VisualizeSerializedState>({
panelType: VISUALIZE_EMBEDDABLE_TYPE,
initialState: { savedObjectId: savedObject.id },
});
},
embeddableType: VISUALIZE_EMBEDDABLE_TYPE,
savedObjectType: VISUALIZE_EMBEDDABLE_TYPE,
savedObjectName: i18n.translate('visualizations.visualizeSavedObjectName', {
defaultMessage: 'Visualization',
}),
getIconForSavedObject: (savedObject) => {
const visState = JSON.parse(savedObject.attributes.visState ?? '{}');
return getTypes().get(visState.type)?.icon ?? '';
},
});

contentManagement.registry.register({
id: CONTENT_ID,
Expand All @@ -411,37 +435,6 @@ export class VisualizationsPlugin
name: 'Visualize Library',
});

embeddable.registerSavedObjectToPanelMethod<
VisualizationSavedObjectAttributes,
VisualizeByValueInput
>(CONTENT_ID, (savedObject) => {
const visState = savedObject.attributes.visState;

// not sure if visState actually is ever undefined, but following the type
if (!savedObject.managed || !visState) {
return {
savedObjectId: savedObject.id,
};
}

// data is not always defined, so I added a default value since the extract
// routine in the embeddable factory expects it to be there
const savedVis = JSON.parse(visState) as Omit<SerializedVis, 'data'> & {
data?: SerializedVisData;
};

if (!savedVis.data) {
savedVis.data = {
searchSource: {},
aggs: [],
};
}

return {
savedVis: savedVis as SerializedVis, // now we're sure we have "data" prop
};
});

return {
...this.types.setup(),
visEditorsRegistry,
Expand All @@ -456,14 +449,15 @@ export class VisualizationsPlugin
expressions,
uiActions,
embeddable,
savedObjects,
spaces,
savedObjectsTaggingOss,
fieldFormats,
usageCollection,
savedObjectsManagement,
contentManagement,
savedSearch,
dataViews,
inspector,
}: VisualizationsStartDeps
): VisualizationsStart {
const types = this.types.start();
Expand All @@ -488,6 +482,8 @@ export class VisualizationsPlugin
setSavedObjectsManagement(savedObjectsManagement);
setContentManagement(contentManagement);
setSavedSearch(savedSearch);
setDataViews(dataViews);
setInspector(inspector);

if (spaces) {
setSpaces(spaces);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

import { SerializedVis } from '../vis';
import { createVisAsync } from '../vis_async';
import { getSavedSearch } from '../services';

export const createVisInstance = async (serializedVis: SerializedVis) => {
const vis = await createVisAsync(serializedVis.type, serializedVis);
if (serializedVis.data.savedSearchId) {
const savedSearch = await getSavedSearch().get(serializedVis.data.savedSearchId);
const indexPattern = savedSearch.searchSource.getField('index');
if (indexPattern) {
vis.data.indexPattern = indexPattern;
vis.data.searchSource?.setField('index', indexPattern);
}
}
return vis;
};
Loading

0 comments on commit ee28e20

Please sign in to comment.