Skip to content

Commit

Permalink
[Canvas] Cleanup services (elastic#194634)
Browse files Browse the repository at this point in the history
Closes elastic#194050

## Summary

This PR refactors the Canvas services to no longer use the
`PluginServiceProvider` from the `PresentationUtil` plugin. Note that
the Canvas storybooks are broken on main (and they have been for who
knows how long) and so, while I did make some changes to the storybooks
to make them **compile**, I didn't bother to get them fully functional.

Note that the Ecommerce workpad is broken - this is not due to this PR,
it is a [bug](elastic#195297) that is
present on main.

### 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

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)


<!--ONMERGE {"backportTargets":["8.x"]} ONMERGE-->

---------

Co-authored-by: Catherine Liu <catherine.liu@elastic.co>
Heenawter and cqliu1 authored Oct 8, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 942a1f1 commit 91c045d
Showing 123 changed files with 989 additions and 2,018 deletions.
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@ import React, { FC } from 'react';
import ReactDOM from 'react-dom';
import { useSearchApi } from '@kbn/presentation-publishing';
import { omit } from 'lodash';
import { pluginServices } from '../../../public/services';
import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib';
import { RendererStrings } from '../../../i18n';
import {
@@ -32,6 +31,7 @@ import { EmbeddableExpression } from '../../expression_types/embeddable';
import { StartDeps } from '../../plugin';
import { embeddableInputToExpression } from './embeddable_input_to_expression';
import { useGetAppContext } from './use_get_app_context';
import { embeddableService } from '../../../public/services/kibana_services';

const { embeddable: strings } = RendererStrings;

@@ -132,13 +132,12 @@ export const embeddableRendererFactory = (
help: strings.getHelpDescription(),
reuseDomNode: true,
render: async (domNode, { input, embeddableType, canvasApi }, handlers) => {
const { embeddables } = pluginServices.getServices();
const uniqueId = handlers.getElementId();
const isByValueEnabled = plugins.presentationUtil.labsService.isProjectEnabled(
'labs:canvas:byValueEmbeddable'
);

if (embeddables.reactEmbeddableRegistryHasKey(embeddableType)) {
if (embeddableService.reactEmbeddableRegistryHasKey(embeddableType)) {
/**
* Prioritize React embeddables
*/
1 change: 1 addition & 0 deletions x-pack/plugins/canvas/jest.config.js
Original file line number Diff line number Diff line change
@@ -17,4 +17,5 @@ module.exports = {
collectCoverageFrom: [
'<rootDir>/x-pack/plugins/canvas/{canvas_plugin_src,common,i18n,public,server,shareable_runtime}/**/*.{js,ts,tsx}',
],
setupFiles: ['<rootDir>/x-pack/plugins/canvas/jest_setup.ts'],
};
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
* 2.0.
*/

export interface CanvasNavLinkService {
updatePath: (path: string) => void;
}
import { setStubKibanaServices } from './public/services/mocks';

// Start the kibana services with stubs
setStubKibanaServices();
25 changes: 8 additions & 17 deletions x-pack/plugins/canvas/public/application.tsx
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@ import { AppMountParameters, CoreStart, CoreSetup, AppUpdater } from '@kbn/core/

import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { PluginServices } from '@kbn/presentation-util-plugin/public';

import { CanvasStartDeps, CanvasSetupDeps } from './plugin';
import { App } from './components/app';
@@ -32,12 +31,7 @@ import { init as initStatsReporter } from './lib/ui_metric';

import { CapabilitiesStrings } from '../i18n';

import {
startLegacyServices,
services,
LegacyServicesProvider,
CanvasPluginServices,
} from './services';
import { startLegacyServices, services, LegacyServicesProvider } from './services';
import { initFunctions } from './functions';
// @ts-expect-error untyped local
import { appUnload } from './state/actions/app';
@@ -56,29 +50,26 @@ export const renderApp = ({
startPlugins,
params,
canvasStore,
pluginServices,
appUpdater,
}: {
coreStart: CoreStart;
startPlugins: CanvasStartDeps;
params: AppMountParameters;
canvasStore: Store;
pluginServices: PluginServices<CanvasPluginServices>;
appUpdater: BehaviorSubject<AppUpdater>;
}) => {
const { element } = params;
element.classList.add('canvas');
element.classList.add('canvasContainerWrapper');
const ServicesContextProvider = pluginServices.getContextProvider();

ReactDOM.render(
<KibanaRenderContextProvider {...coreStart}>
<KibanaContextProvider services={{ ...startPlugins, ...coreStart }}>
<ServicesContextProvider>
<LegacyServicesProvider providers={services}>
<Provider store={canvasStore}>
<App history={params.history} />
</Provider>
</LegacyServicesProvider>
</ServicesContextProvider>
<LegacyServicesProvider providers={services}>
<Provider store={canvasStore}>
<App history={params.history} appUpdater={appUpdater} />
</Provider>
</LegacyServicesProvider>
</KibanaContextProvider>
</KibanaRenderContextProvider>,
element
26 changes: 19 additions & 7 deletions x-pack/plugins/canvas/public/components/app/index.tsx
Original file line number Diff line number Diff line change
@@ -5,14 +5,17 @@
* 2.0.
*/

import React, { FC, useEffect } from 'react';
import { AppUpdater, ScopedHistory } from '@kbn/core/public';
import PropTypes from 'prop-types';
import { ScopedHistory } from '@kbn/core/public';
import { useNavLinkService } from '../../services';
import React, { FC, useEffect } from 'react';
import { BehaviorSubject } from 'rxjs';
// @ts-expect-error
import { shortcutManager } from '../../lib/shortcut_manager';
import { CanvasRouter } from '../../routes';
import { Flyouts } from '../flyouts';
import { getSessionStorage } from '../../lib/storage';
import { SESSIONSTORAGE_LASTPATH } from '../../../common/lib';
import { coreServices } from '../../services/kibana_services';

class ShortcutManagerContextWrapper extends React.Component<React.PropsWithChildren<{}>> {
static childContextTypes = {
@@ -28,12 +31,21 @@ class ShortcutManagerContextWrapper extends React.Component<React.PropsWithChild
}
}

export const App: FC<{ history: ScopedHistory }> = ({ history }) => {
const { updatePath } = useNavLinkService();

export const App: FC<{ history: ScopedHistory; appUpdater: BehaviorSubject<AppUpdater> }> = ({
history,
appUpdater,
}) => {
useEffect(() => {
return history.listen(({ pathname, search }) => {
updatePath(pathname + search);
const path = pathname + search;
appUpdater.next(() => ({
defaultPath: path,
}));

getSessionStorage().set(
`${SESSIONSTORAGE_LASTPATH}:${coreServices.http.basePath.get()}`,
path
);
});
});

Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ import { State, AssetType, CanvasWorkpad } from '../../../types';

import { AssetManager as Component } from './asset_manager.component';
import { getFullWorkpadPersisted } from '../../state/selectors/workpad';
import { pluginServices } from '../../services';
import { getCanvasWorkpadService } from '../../services/canvas_workpad_service';

export const AssetManager = connect(
(state: State) => ({
@@ -31,7 +31,7 @@ export const AssetManager = connect(
onAddAsset: (workpad: CanvasWorkpad, type: AssetType['type'], content: AssetType['value']) => {
// make the ID here and pass it into the action
const asset = createAsset(type, content);
const { notify, workpad: workpadService } = pluginServices.getServices();
const workpadService = getCanvasWorkpadService();

return workpadService
.updateAssets(workpad.id, { ...workpad.assets, [asset.id]: asset })
@@ -40,7 +40,7 @@ export const AssetManager = connect(
// then return the id, so the caller knows the id that will be created
return asset.id;
})
.catch((error) => notifyError(error, notify.error));
.catch((error) => notifyError(error));
},
}),
(stateProps, dispatchProps, ownProps) => {
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ import {
import { isEqual } from 'lodash';
import { i18n } from '@kbn/i18n';

import { pluginServices } from '../../services';
import { dataViewsService } from '../../services/kibana_services';
import { DatasourceSelector } from './datasource_selector';
import { DatasourcePreview } from './datasource_preview';

@@ -67,12 +67,9 @@ export class DatasourceComponent extends PureComponent {
state = { defaultIndex: '' };

componentDidMount() {
pluginServices
.getServices()
.dataViews.getDefaultDataView()
.then((defaultDataView) => {
this.setState({ defaultIndex: defaultDataView.title });
});
dataViewsService.getDefaultDataView().then((defaultDataView) => {
this.setState({ defaultIndex: defaultDataView.title });
});
}

componentDidUpdate(prevProps) {
Original file line number Diff line number Diff line change
@@ -8,18 +8,17 @@
import React, { useState, useEffect } from 'react';
import { PropTypes } from 'prop-types';
import { Loading } from '../../loading';
import { useExpressionsService } from '../../../services';
import { getCanvasExpressionService } from '../../../services/canvas_expressions_service';
import { DatasourcePreview as Component } from './datasource_preview';

export const DatasourcePreview = (props) => {
const [datatable, setDatatable] = useState();
const expressionsService = useExpressionsService();

useEffect(() => {
expressionsService
getCanvasExpressionService()
.interpretAst({ type: 'expression', chain: [props.function] }, {})
.then(setDatatable);
}, [expressionsService, props.function, setDatatable]);
}, [props.function, setDatatable]);

if (!datatable) {
return <Loading {...props} />;
Original file line number Diff line number Diff line change
@@ -5,23 +5,24 @@
* 2.0.
*/

import React from 'react';
import React, { useMemo } from 'react';
import { useSelector } from 'react-redux';
import { getSelectedPage, getPageById } from '../../state/selectors/workpad';
import { useExpressionsService } from '../../services';
import { ElementContent as Component, Props as ComponentProps } from './element_content';
import { State } from '../../../types';
import { getCanvasExpressionService } from '../../services/canvas_expressions_service';

export type Props = Omit<ComponentProps, 'renderFunction' | 'backgroundColor'>;

export const ElementContent = (props: Props) => {
const expressionsService = useExpressionsService();
const selectedPageId = useSelector(getSelectedPage);
const backgroundColor =
useSelector((state: State) => getPageById(state, selectedPageId)?.style.background) || '';
const { renderable } = props;

const renderFunction = renderable ? expressionsService.getRenderer(renderable.as) : null;
const renderFunction = useMemo(() => {
return renderable ? getCanvasExpressionService().getRenderer(renderable.as) : null;
}, [renderable]);

return <Component {...{ ...props, renderFunction, backgroundColor }} />;
};
Original file line number Diff line number Diff line change
@@ -5,14 +5,18 @@
* 2.0.
*/

import React, { FC, useCallback, useMemo } from 'react';
import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui';
import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { FC, useCallback, useMemo } from 'react';

import { SavedObjectFinder, SavedObjectMetaData } from '@kbn/saved-objects-finder-plugin/public';
import { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common';
import { EmbeddableFactory, ReactEmbeddableSavedObject } from '@kbn/embeddable-plugin/public';
import { useEmbeddablesService, usePlatformService } from '../../services';
import { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common';
import { SavedObjectFinder, SavedObjectMetaData } from '@kbn/saved-objects-finder-plugin/public';
import {
contentManagementService,
coreServices,
embeddableService,
} from '../../services/kibana_services';

const strings = {
getNoItemsText: () =>
@@ -45,13 +49,8 @@ export const AddEmbeddableFlyout: FC<Props> = ({
onClose,
isByValueEnabled,
}) => {
const embeddablesService = useEmbeddablesService();
const platformService = usePlatformService();
const { getEmbeddableFactories, getReactEmbeddableSavedObjects } = embeddablesService;
const { getContentManagement, getUISettings } = platformService;

const legacyFactoriesBySavedObjectType: LegacyFactoryMap = useMemo(() => {
return [...getEmbeddableFactories()]
return [...embeddableService.getEmbeddableFactories()]
.filter(
(embeddableFactory) =>
Boolean(embeddableFactory.savedObjectMetaData?.type) && !embeddableFactory.isContainerType
@@ -60,10 +59,10 @@ export const AddEmbeddableFlyout: FC<Props> = ({
acc[factory.savedObjectMetaData!.type] = factory;
return acc;
}, {} as LegacyFactoryMap);
}, [getEmbeddableFactories]);
}, []);

const factoriesBySavedObjectType: FactoryMap = useMemo(() => {
return [...getReactEmbeddableSavedObjects()]
return [...embeddableService.getReactEmbeddableSavedObjects()]
.filter(([type, embeddableFactory]) => {
return Boolean(embeddableFactory.savedObjectMetaData?.type);
})
@@ -74,7 +73,7 @@ export const AddEmbeddableFlyout: FC<Props> = ({
};
return acc;
}, {} as FactoryMap);
}, [getReactEmbeddableSavedObjects]);
}, []);

const metaData = useMemo(
() =>
@@ -111,7 +110,7 @@ export const AddEmbeddableFlyout: FC<Props> = ({
onSelect(id, type, isByValueEnabled);
return;
}
const embeddableFactories = getEmbeddableFactories();
const embeddableFactories = embeddableService.getEmbeddableFactories();
// Find the embeddable type from the saved object type
const found = Array.from(embeddableFactories).find((embeddableFactory) => {
return Boolean(
@@ -124,7 +123,7 @@ export const AddEmbeddableFlyout: FC<Props> = ({

onSelect(id, foundEmbeddableType, isByValueEnabled);
},
[isByValueEnabled, getEmbeddableFactories, onSelect, factoriesBySavedObjectType]
[isByValueEnabled, onSelect, factoriesBySavedObjectType]
);

return (
@@ -141,8 +140,8 @@ export const AddEmbeddableFlyout: FC<Props> = ({
showFilter={true}
noItemsMessage={strings.getNoItemsText()}
services={{
contentClient: getContentManagement().client,
uiSettings: getUISettings(),
contentClient: contentManagementService.client,
uiSettings: coreServices.uiSettings,
}}
/>
</EuiFlyoutBody>
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ import { getSelectedPage } from '../../state/selectors/workpad';
import { EmbeddableTypes } from '../../../canvas_plugin_src/expression_types/embeddable';
import { embeddableInputToExpression } from '../../../canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression';
import { State } from '../../../types';
import { useLabsService } from '../../services';
import { presentationUtilService } from '../../services/kibana_services';

const allowedEmbeddables = {
[EmbeddableTypes.map]: (id: string) => {
@@ -67,8 +67,9 @@ export const AddEmbeddablePanel: React.FunctionComponent<FlyoutProps> = ({
availableEmbeddables,
...restProps
}) => {
const labsService = useLabsService();
const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable');
const isByValueEnabled = presentationUtilService.labsService.isProjectEnabled(
'labs:canvas:byValueEmbeddable'
);

const dispatch = useDispatch();
const pageId = useSelector<State, string>((state) => getSelectedPage(state));
Original file line number Diff line number Diff line change
@@ -7,14 +7,12 @@

import React, { FocusEventHandler } from 'react';
import { EuiComboBox } from '@elastic/eui';
import { DataView } from '@kbn/data-views-plugin/common';

type DataViewOption = Pick<DataView, 'id' | 'name' | 'title'>;
import { DataViewListItem } from '@kbn/data-views-plugin/common';

export interface ESDataViewSelectProps {
loading: boolean;
value: string;
dataViews: DataViewOption[];
dataViews: DataViewListItem[];
onChange: (string: string) => void;
onBlur: FocusEventHandler<HTMLDivElement> | undefined;
onFocus: FocusEventHandler<HTMLDivElement> | undefined;
@@ -31,7 +29,7 @@ export const ESDataViewSelect: React.FunctionComponent<ESDataViewSelectProps> =
onFocus,
onBlur,
}) => {
const selectedDataView = dataViews.find((view) => value === view.title) as DataViewOption;
const selectedDataView = dataViews.find((view) => value === view.title);

const selectedOption = selectedDataView
? { value: selectedDataView.title, label: selectedDataView.name || selectedDataView.title }
Loading

0 comments on commit 91c045d

Please sign in to comment.