[8.x] [Canvas] Cleanup services (#194634) (#195639)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Canvas] Cleanup services
(#194634)](https://github.com/elastic/kibana/pull/194634)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Hannah
Mudge","email":"Heenawter@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-08T20:34:01Z","message":"[Canvas]
Cleanup services (#194634)\n\nCloses
https://github.com/elastic/kibana/issues/194050\r\n\r\n##
Summary\r\n\r\nThis PR refactors the Canvas services to no longer use
the\r\n`PluginServiceProvider` from the `PresentationUtil` plugin. Note
that\r\nthe Canvas storybooks are broken on main (and they have been for
who\r\nknows how long) and so, while I did make some changes to the
storybooks\r\nto make them **compile**, I didn't bother to get them
fully functional.\r\n\r\nNote that the Ecommerce workpad is broken -
this is not due to this PR,\r\nit is a
[bug](https://github.com/elastic/kibana/issues/195297) that
is\r\npresent on main.\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n\r\n<!--ONMERGE
{\"backportTargets\":[\"8.x\"]}
ONMERGE-->\r\n\r\n---------\r\n\r\nCo-authored-by: Catherine Liu
<catherine.liu@elastic.co>","sha":"91c045d698b2e68afd13f5d4bef9229d8a231abe","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Presentation","loe:medium","technical
debt","release_note:skip","impact:high","v9.0.0","backport:prev-minor"],"number":194634,"url":"https://github.com/elastic/kibana/pull/194634","mergeCommit":{"message":"[Canvas]
Cleanup services (#194634)\n\nCloses
https://github.com/elastic/kibana/issues/194050\r\n\r\n##
Summary\r\n\r\nThis PR refactors the Canvas services to no longer use
the\r\n`PluginServiceProvider` from the `PresentationUtil` plugin. Note
that\r\nthe Canvas storybooks are broken on main (and they have been for
who\r\nknows how long) and so, while I did make some changes to the
storybooks\r\nto make them **compile**, I didn't bother to get them
fully functional.\r\n\r\nNote that the Ecommerce workpad is broken -
this is not due to this PR,\r\nit is a
[bug](https://github.com/elastic/kibana/issues/195297) that
is\r\npresent on main.\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n\r\n<!--ONMERGE
{\"backportTargets\":[\"8.x\"]}
ONMERGE-->\r\n\r\n---------\r\n\r\nCo-authored-by: Catherine Liu
<catherine.liu@elastic.co>","sha":"91c045d698b2e68afd13f5d4bef9229d8a231abe"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194634","number":194634,"mergeCommit":{"message":"[Canvas]
Cleanup services (#194634)\n\nCloses
https://github.com/elastic/kibana/issues/194050\r\n\r\n##
Summary\r\n\r\nThis PR refactors the Canvas services to no longer use
the\r\n`PluginServiceProvider` from the `PresentationUtil` plugin. Note
that\r\nthe Canvas storybooks are broken on main (and they have been for
who\r\nknows how long) and so, while I did make some changes to the
storybooks\r\nto make them **compile**, I didn't bother to get them
fully functional.\r\n\r\nNote that the Ecommerce workpad is broken -
this is not due to this PR,\r\nit is a
[bug](https://github.com/elastic/kibana/issues/195297) that
is\r\npresent on main.\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n\r\n<!--ONMERGE
{\"backportTargets\":[\"8.x\"]}
ONMERGE-->\r\n\r\n---------\r\n\r\nCo-authored-by: Catherine Liu
<catherine.liu@elastic.co>","sha":"91c045d698b2e68afd13f5d4bef9229d8a231abe"}}]}]
BACKPORT-->
This commit is contained in:
Hannah Mudge 2024-10-09 12:11:00 -06:00 committed by GitHub
parent e8992e3749
commit a177282289
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
123 changed files with 983 additions and 2012 deletions

View file

@ -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
*/

View file

@ -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'],
};

View file

@ -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();

View file

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

View file

@ -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
);
});
});

View file

@ -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) => {

View file

@ -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) {

View file

@ -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} />;

View file

@ -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 }} />;
};

View file

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

View file

@ -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));

View file

@ -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 }

View file

@ -5,25 +5,24 @@
* 2.0.
*/
import { DataView } from '@kbn/data-views-plugin/common';
import { DataViewListItem } from '@kbn/data-views-plugin/common';
import { sortBy } from 'lodash';
import React, { FC, useRef, useState } from 'react';
import useEffectOnce from 'react-use/lib/useEffectOnce';
import { useDataViewsService } from '../../services';
import {
ESDataViewSelect as Component,
ESDataViewSelectProps as Props,
} from './es_data_view_select.component';
import { getDataViews } from '../../lib/data_view_helpers';
type ESDataViewSelectProps = Omit<Props, 'indices' | 'loading'>;
export const ESDataViewSelect: FC<ESDataViewSelectProps> = (props) => {
const { value, onChange } = props;
const [dataViews, setDataViews] = useState<Array<Pick<DataView, 'id' | 'name' | 'title'>>>([]);
const [dataViews, setDataViews] = useState<DataViewListItem[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const mounted = useRef(true);
const { getDataViews } = useDataViewsService();
useEffectOnce(() => {
getDataViews().then((newDataViews) => {

View file

@ -6,8 +6,8 @@
*/
import React, { useState, useEffect, useRef } from 'react';
import { useDataViewsService } from '../../services';
import { ESFieldSelect as Component, ESFieldSelectProps as Props } from './es_field_select';
import { getDataViewFields } from '../../lib/data_view_helpers';
type ESFieldSelectProps = Omit<Props, 'fields'>;
@ -15,17 +15,16 @@ export const ESFieldSelect: React.FunctionComponent<ESFieldSelectProps> = (props
const { index, value, onChange } = props;
const [fields, setFields] = useState<string[]>([]);
const loadingFields = useRef(false);
const { getFields } = useDataViewsService();
useEffect(() => {
loadingFields.current = true;
getFields(index)
getDataViewFields(index)
.then((newFields) => setFields(newFields || []))
.finally(() => {
loadingFields.current = false;
});
}, [index, getFields]);
}, [index]);
useEffect(() => {
if (!loadingFields.current && value && !fields.includes(value)) {

View file

@ -8,11 +8,11 @@
import React, { useState, useEffect, useRef } from 'react';
import { isEqual } from 'lodash';
import usePrevious from 'react-use/lib/usePrevious';
import { useDataViewsService } from '../../services';
import {
ESFieldsSelect as Component,
ESFieldsSelectProps as Props,
} from './es_fields_select.component';
import { getDataViewFields } from '../../lib/data_view_helpers';
type ESFieldsSelectProps = Omit<Props, 'fields'> & { index: string };
@ -21,11 +21,10 @@ export const ESFieldsSelect: React.FunctionComponent<ESFieldsSelectProps> = (pro
const [fields, setFields] = useState<string[]>([]);
const prevIndex = usePrevious(index);
const mounted = useRef(true);
const { getFields } = useDataViewsService();
useEffect(() => {
if (prevIndex !== index) {
getFields(index).then((newFields) => {
getDataViewFields(index).then((newFields) => {
if (!mounted.current) {
return;
}
@ -37,7 +36,7 @@ export const ESFieldsSelect: React.FunctionComponent<ESFieldsSelectProps> = (pro
}
});
}
}, [fields, index, onChange, prevIndex, selected, getFields]);
}, [fields, index, onChange, prevIndex, selected]);
useEffect(
() => () => {

View file

@ -8,7 +8,6 @@
import React, { FC, useState, useCallback, useMemo, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fromExpression } from '@kbn/interpreter';
import { useExpressionsService } from '../../services';
import { getSelectedPage, getSelectedElement } from '../../state/selectors/workpad';
// @ts-expect-error
import { setExpression, flushContext } from '../../state/actions/elements';
@ -16,6 +15,7 @@ import { setExpression, flushContext } from '../../state/actions/elements';
import { ElementNotSelected } from './element_not_selected';
import { Expression as Component } from './expression';
import { State, CanvasElement } from '../../../types';
import { getCanvasExpressionService } from '../../services/canvas_expressions_service';
interface ExpressionProps {
done: () => void;
@ -45,7 +45,6 @@ export const Expression: FC<ExpressionProps> = ({ done }) => {
};
const ExpressionContainer: FC<ExpressionContainerProps> = ({ done, element, pageId }) => {
const expressions = useExpressionsService();
const dispatch = useDispatch();
const [isCompact, setCompact] = useState<boolean>(true);
const toggleCompactView = useCallback(() => {
@ -111,8 +110,8 @@ const ExpressionContainer: FC<ExpressionContainerProps> = ({ done, element, page
}, [element, setFormState, formState]);
const functionDefinitions = useMemo(
() => Object.values(expressions.getFunctions()),
[expressions]
() => Object.values(getCanvasExpressionService().getFunctions()),
[]
);
return (

View file

@ -34,8 +34,8 @@ import { findExistingAsset } from '../../lib/find_existing_asset';
import { FunctionForm as Component } from './function_form';
import { Args, ArgType, ArgTypeDef } from '../../expression_types/types';
import { State, ExpressionContext, CanvasElement, AssetType } from '../../../types';
import { useNotifyService, useWorkpadService } from '../../services';
import { createAsset, notifyError } from '../../lib/assets';
import { getCanvasWorkpadService } from '../../services/canvas_workpad_service';
interface FunctionFormProps {
name: string;
@ -54,8 +54,6 @@ interface FunctionFormProps {
export const FunctionForm: React.FunctionComponent<FunctionFormProps> = (props) => {
const { expressionIndex, ...restProps } = props;
const { nextArgType, path, parentPath, argType } = restProps;
const service = useWorkpadService();
const notifyService = useNotifyService();
const dispatch = useDispatch();
const context = useSelector<State, ExpressionContext>(
@ -113,16 +111,16 @@ export const FunctionForm: React.FunctionComponent<FunctionFormProps> = (props)
// make the ID here and pass it into the action
const asset = createAsset(type, content);
return service
return getCanvasWorkpadService()
.updateAssets(workpad.id, { ...workpad.assets, [asset.id]: asset })
.then((res) => {
dispatch(setAsset(asset));
// then return the id, so the caller knows the id that will be created
return asset.id;
})
.catch((error) => notifyError(error, notifyService.error));
.catch((error) => notifyError(error));
},
[dispatch, notifyService, service, workpad.assets, workpad.id]
[dispatch, workpad.assets, workpad.id]
);
const onAssetAdd = useCallback(

View file

@ -8,7 +8,7 @@
import { compose, withProps } from 'react-recompose';
import { get } from 'lodash';
import { toExpression } from '@kbn/interpreter';
import { pluginServices } from '../../services';
import { getCanvasExpressionService } from '../../services/canvas_expressions_service';
import { getArgTypeDef } from '../../lib/args';
import { FunctionFormList as Component } from './function_form_list';
@ -78,7 +78,7 @@ const componentFactory = ({
parentPath,
removable,
}) => {
const { expressions } = pluginServices.getServices();
const expressions = getCanvasExpressionService();
return {
args,
nestedFunctionsArgs: argsWithExprFunctions,

View file

@ -8,7 +8,7 @@
import React from 'react';
import { reduxDecorator } from '../../../storybook';
import { argTypes } from '../../services/storybook';
import { argTypes } from '../../../storybook/constants';
import { Home } from './home';

View file

@ -9,16 +9,17 @@ import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { useNotifyService, useWorkpadService } from '../../../services';
import { useNotifyService } from '../../../services';
import { getId } from '../../../lib/get_id';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
export const useCloneWorkpad = () => {
const workpadService = useWorkpadService();
const notifyService = useNotifyService();
const history = useHistory();
return useCallback(
async (workpadId: string) => {
const workpadService = getCanvasWorkpadService();
try {
let workpad = await workpadService.get(workpadId);
@ -35,7 +36,7 @@ export const useCloneWorkpad = () => {
notifyService.error(err, { title: errors.getCloneFailureErrorMessage() });
}
},
[notifyService, workpadService, history]
[notifyService, history]
);
};

View file

@ -9,15 +9,16 @@ import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { CanvasTemplate } from '../../../../types';
import { useNotifyService, useWorkpadService } from '../../../services';
import { useNotifyService } from '../../../services';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
export const useCreateFromTemplate = () => {
const workpadService = useWorkpadService();
const notifyService = useNotifyService();
const history = useHistory();
return useCallback(
async (template: CanvasTemplate) => {
const workpadService = getCanvasWorkpadService();
try {
const result = await workpadService.createFromTemplate(template.id);
history.push(`/workpad/${result.id}/page/1`);
@ -27,6 +28,6 @@ export const useCreateFromTemplate = () => {
});
}
},
[workpadService, notifyService, history]
[notifyService, history]
);
};

View file

@ -11,17 +11,18 @@ import { i18n } from '@kbn/i18n';
// @ts-expect-error
import { getDefaultWorkpad } from '../../../state/defaults';
import { useNotifyService, useWorkpadService } from '../../../services';
import { useNotifyService } from '../../../services';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
import type { CanvasWorkpad } from '../../../../types';
export const useCreateWorkpad = () => {
const workpadService = useWorkpadService();
const notifyService = useNotifyService();
const history = useHistory();
return useCallback(
async (_workpad?: CanvasWorkpad | null) => {
const workpadService = getCanvasWorkpadService();
const workpad = _workpad || (getDefaultWorkpad() as CanvasWorkpad);
try {
@ -34,7 +35,7 @@ export const useCreateWorkpad = () => {
}
return;
},
[notifyService, history, workpadService]
[notifyService, history]
);
};

View file

@ -8,51 +8,48 @@
import { useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { useNotifyService, useWorkpadService } from '../../../services';
import { getCanvasNotifyService } from '../../../services/canvas_notify_service';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
export const useDeleteWorkpads = () => {
const workpadService = useWorkpadService();
const notifyService = useNotifyService();
return useCallback(async (workpadIds: string[]) => {
const workpadService = getCanvasWorkpadService();
return useCallback(
async (workpadIds: string[]) => {
const removedWorkpads = workpadIds.map(async (id) => {
try {
await workpadService.remove(id);
return { id, err: null };
} catch (err) {
return { id, err };
}
});
const removedWorkpads = workpadIds.map(async (id) => {
try {
await workpadService.remove(id);
return { id, err: null };
} catch (err) {
return { id, err };
}
});
return Promise.all(removedWorkpads).then((results) => {
const [passes, errored] = results.reduce<[string[], string[]]>(
([passesArr, errorsArr], result) => {
if (result.err) {
errorsArr.push(result.id);
} else {
passesArr.push(result.id);
}
return Promise.all(removedWorkpads).then((results) => {
const [passes, errored] = results.reduce<[string[], string[]]>(
([passesArr, errorsArr], result) => {
if (result.err) {
errorsArr.push(result.id);
} else {
passesArr.push(result.id);
}
return [passesArr, errorsArr];
},
[[], []]
);
return [passesArr, errorsArr];
},
[[], []]
);
const removedIds = workpadIds.filter((id) => passes.includes(id));
const removedIds = workpadIds.filter((id) => passes.includes(id));
if (errored.length > 0) {
notifyService.error(errors.getDeleteFailureErrorMessage());
}
if (errored.length > 0) {
getCanvasNotifyService().error(errors.getDeleteFailureErrorMessage());
}
return {
removedIds,
errored,
};
});
},
[workpadService, notifyService]
);
return {
removedIds,
errored,
};
});
}, []);
};
const errors = {

View file

@ -6,10 +6,8 @@
*/
import { useCallback } from 'react';
import { useWorkpadService } from '../../../services';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
export const useFindTemplates = () => {
const workpadService = useWorkpadService();
return useCallback(async () => await workpadService.findTemplates(), [workpadService]);
return useCallback(async () => await getCanvasWorkpadService().findTemplates(), []);
};

View file

@ -8,21 +8,21 @@
import { useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { useNotifyService, useWorkpadService } from '../../../services';
import { useNotifyService } from '../../../services';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
export const useFindWorkpads = () => {
const workpadService = useWorkpadService();
const notifyService = useNotifyService();
return useCallback(
async (text = '') => {
try {
return await workpadService.find(text);
return await getCanvasWorkpadService().find(text);
} catch (err) {
notifyService.error(err, { title: errors.getFindFailureErrorMessage() });
}
},
[notifyService, workpadService]
[notifyService]
);
};

View file

@ -9,17 +9,18 @@ import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { useNotifyService, useWorkpadService } from '../../../services';
import { useNotifyService } from '../../../services';
import type { CanvasWorkpad } from '../../../../types';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
export const useImportWorkpad = () => {
const workpadService = useWorkpadService();
const notifyService = useNotifyService();
const history = useHistory();
return useCallback(
async (workpad: CanvasWorkpad) => {
const workpadService = getCanvasWorkpadService();
try {
const importedWorkpad = await workpadService.import(workpad);
history.push(`/workpad/${importedWorkpad.id}/page/1`);
@ -30,7 +31,7 @@ export const useImportWorkpad = () => {
}
return;
},
[notifyService, history, workpadService]
[notifyService, history]
);
};

View file

@ -8,7 +8,7 @@
import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FoundWorkpad } from '../../../services/workpad';
import { FoundWorkpad } from '../../../services/canvas_workpad_service';
import { UploadDropzone } from './upload_dropzone';
import { HomeEmptyPrompt } from './empty_prompt';
import { WorkpadTable } from './workpad_table';

View file

@ -9,7 +9,7 @@ import React from 'react';
import { EuiPanel } from '@elastic/eui';
import { reduxDecorator } from '../../../../storybook';
import { argTypes } from '../../../services/storybook';
import { argTypes } from '../../../../storybook/constants';
import { MyWorkpads as Component } from './my_workpads';

View file

@ -7,7 +7,7 @@
import React, { useState, useEffect, createContext, Dispatch, SetStateAction } from 'react';
import { useFindWorkpads } from '../hooks';
import { FoundWorkpad } from '../../../services/workpad';
import { FoundWorkpad } from '../../../services/canvas_workpad_service';
import { Loading } from '../loading';
import { MyWorkpads as Component } from './my_workpads.component';

View file

@ -21,7 +21,7 @@ import {
import moment from 'moment';
import { RoutingLink } from '../../routing';
import { FoundWorkpad } from '../../../services/workpad';
import { FoundWorkpad } from '../../../services/canvas_workpad_service';
import { WorkpadTableTools } from './workpad_table_tools';
import { WorkpadImport } from './workpad_import';

View file

@ -9,9 +9,9 @@ import React, { useState, useEffect } from 'react';
import { EuiPanel } from '@elastic/eui';
import { reduxDecorator } from '../../../../storybook';
import { argTypes } from '../../../../storybook/constants';
import { argTypes } from '../../../services/storybook';
import { getSomeWorkpads } from '../../../services/stubs/workpad';
import { getSomeWorkpads } from '../../../services/mocks';
import { WorkpadTable as Component } from './workpad_table';
import { WorkpadsContext } from './my_workpads';

View file

@ -8,17 +8,16 @@
import React, { useContext } from 'react';
import { useSelector } from 'react-redux';
import { canUserWrite as canUserWriteSelector } from '../../../state/selectors/app';
import type { State } from '../../../../types';
import { usePlatformService } from '../../../services';
import { useCloneWorkpad } from '../hooks';
import { canUserWrite as canUserWriteSelector } from '../../../state/selectors/app';
import { useDownloadWorkpad } from '../../hooks';
import { useCloneWorkpad } from '../hooks';
import { WorkpadTable as Component } from './workpad_table.component';
import { coreServices } from '../../../services/kibana_services';
import { WorkpadsContext } from './my_workpads';
import { WorkpadTable as Component } from './workpad_table.component';
export const WorkpadTable = () => {
const platformService = usePlatformService();
const onCloneWorkpad = useCloneWorkpad();
const onExportWorkpad = useDownloadWorkpad();
const context = useContext(WorkpadsContext);
@ -33,7 +32,7 @@ export const WorkpadTable = () => {
const { workpads } = context;
const dateFormat = platformService.getUISetting('dateFormat');
const dateFormat = coreServices.uiSettings.get('dateFormat');
return <Component {...{ workpads, dateFormat, canUserWrite, onCloneWorkpad, onExportWorkpad }} />;
};

View file

@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import { EuiButton, EuiToolTip, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { ConfirmModal } from '../../confirm_modal';
import { FoundWorkpad } from '../../../services/workpad';
import { FoundWorkpad } from '../../../services/canvas_workpad_service';
export interface Props {
workpads: FoundWorkpad[];

View file

@ -9,7 +9,7 @@ import { EuiPanel } from '@elastic/eui';
import React from 'react';
import { reduxDecorator } from '../../../../storybook';
import { argTypes } from '../../../services/storybook';
import { argTypes } from '../../../../storybook/constants';
import { WorkpadTemplates as Component } from './workpad_templates';

View file

@ -11,17 +11,16 @@ import { useDispatch } from 'react-redux';
import { getBaseBreadcrumb } from '../../lib/breadcrumbs';
import { resetWorkpad } from '../../state/actions/workpad';
import { HomeApp as Component } from './home_app.component';
import { usePlatformService } from '../../services';
import { coreServices } from '../../services/kibana_services';
export const HomeApp = () => {
const { setBreadcrumbs } = usePlatformService();
const dispatch = useDispatch();
const onLoad = () => dispatch(resetWorkpad());
const history = useHistory();
useEffect(() => {
setBreadcrumbs([getBaseBreadcrumb(history)]);
}, [setBreadcrumbs, history]);
coreServices.chrome.setBreadcrumbs([getBaseBreadcrumb(history)]);
}, [history]);
return <Component onLoad={onLoad} />;
};

View file

@ -8,9 +8,10 @@
import { useCallback } from 'react';
import fileSaver from 'file-saver';
import { i18n } from '@kbn/i18n';
import { useNotifyService, useWorkpadService } from '../../../services';
import { useNotifyService } from '../../../services';
import { CanvasWorkpad } from '../../../../types';
import type { CanvasRenderedWorkpad } from '../../../../shareable_runtime/types';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
const strings = {
getDownloadFailureErrorMessage: () =>
@ -28,12 +29,12 @@ const strings = {
export const useDownloadWorkpad = () => {
const notifyService = useNotifyService();
const workpadService = useWorkpadService();
const download = useDownloadWorkpadBlob();
return useCallback(
async (workpadId: string) => {
try {
const workpadService = getCanvasWorkpadService();
const workpad = await workpadService.get(workpadId);
download(workpad, `canvas-workpad-${workpad.name}-${workpad.id}`);
@ -41,7 +42,7 @@ export const useDownloadWorkpad = () => {
notifyService.error(err, { title: strings.getDownloadFailureErrorMessage() });
}
},
[workpadService, notifyService, download]
[notifyService, download]
);
};

View file

@ -12,7 +12,7 @@ import { ErrorStrings } from '../../../../i18n';
import { CANVAS_APP } from '../../../../common/lib';
import { decode } from '../../../../common/lib/embeddable_dataurl';
import { CanvasElement, CanvasPage } from '../../../../types';
import { useEmbeddablesService, useLabsService, useNotifyService } from '../../../services';
import { useNotifyService } from '../../../services';
// @ts-expect-error unconverted file
import { addElement, fetchAllRenderables } from '../../../state/actions/elements';
// @ts-expect-error unconverted file
@ -24,16 +24,16 @@ import {
} from '../../../state/actions/embeddable';
import { clearValue } from '../../../state/actions/resolved_args';
import { embeddableInputToExpression } from '../../../../canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression';
import { embeddableService, presentationUtilService } from '../../../services/kibana_services';
const { actionsElements: strings } = ErrorStrings;
export const useIncomingEmbeddable = (selectedPage: CanvasPage) => {
const embeddablesService = useEmbeddablesService();
const labsService = useLabsService();
const labsService = presentationUtilService.labsService;
const dispatch = useDispatch();
const notifyService = useNotifyService();
const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable');
const stateTransferService = embeddablesService.getStateTransfer();
const stateTransferService = embeddableService.getStateTransfer();
// fetch incoming embeddable from state transfer service.
const incomingEmbeddable = stateTransferService.getIncomingEmbeddablePackage(CANVAS_APP, true);

View file

@ -5,11 +5,11 @@
* 2.0.
*/
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { camelCase } from 'lodash';
import { cloneSubgraphs } from '../../lib/clone_subgraphs';
import { useNotifyService, useCustomElementService } from '../../services';
import { useNotifyService } from '../../services';
// @ts-expect-error untyped local
import { selectToplevelNodes } from '../../state/actions/transient';
// @ts-expect-error untyped local
@ -21,6 +21,7 @@ import {
Props as ComponentProps,
} from './saved_elements_modal.component';
import { PositionedElement, CustomElement } from '../../../types';
import { getCustomElementService } from '../../services/canvas_custom_element_service';
const customElementAdded = 'elements-custom-added';
@ -28,7 +29,7 @@ export type Props = Pick<ComponentProps, 'onClose'>;
export const SavedElementsModal = ({ onClose }: Props) => {
const notifyService = useNotifyService();
const customElementService = useCustomElementService();
const customElementService = useMemo(() => getCustomElementService(), []);
const dispatch = useDispatch();
const pageId = useSelector(getSelectedPage);
const [customElements, setCustomElements] = useState<CustomElement[]>([]);

View file

@ -24,10 +24,10 @@ import { useZoomHandlers } from '../../lib/app_handler_creators';
import { trackCanvasUiMetric, METRIC_TYPE } from '../../lib/ui_metric';
import { LAUNCHED_FULLSCREEN, LAUNCHED_FULLSCREEN_AUTOPLAY } from '../../../common/lib/constants';
import { WorkpadRoutingContext } from '../../routes/workpad';
import { usePlatformService } from '../../services';
import { Workpad as WorkpadComponent, Props } from './workpad.component';
import { State } from '../../../types';
import { useIncomingEmbeddable } from '../hooks';
import { coreServices } from '../../services/kibana_services';
type ContainerProps = Pick<Props, 'registerLayout' | 'unregisterLayout'>;
@ -40,9 +40,7 @@ export const Workpad: FC<ContainerProps> = (props) => {
const { isFullscreen, setFullscreen, undo, redo, autoplayInterval, nextPage, previousPage } =
useContext(WorkpadRoutingContext);
const platformService = usePlatformService();
const hasHeaderBanner = useObservable(platformService.hasHeaderBanner$());
const hasHeaderBanner = useObservable<boolean>(coreServices.chrome.hasHeaderBanner$());
const propsFromState = useSelector((state: State) => {
const { width, height, id: workpadId, css: workpadCss } = getWorkpad(state);

View file

@ -5,17 +5,19 @@
* 2.0.
*/
import { useMemo } from 'react';
import { AstFunction, fromExpression } from '@kbn/interpreter';
import { shallowEqual, useSelector } from 'react-redux';
import { State } from '../../../../types';
import { getFiltersByFilterExpressions } from '../../../lib/filter';
import { adaptCanvasFilter } from '../../../lib/filter_adapters';
import { useFiltersService } from '../../../services';
import { getCanvasFiltersService } from '../../../services/canvas_filters_service';
const extractExpressionAST = (filters: string[]) => fromExpression(filters.join(' | '));
export function useCanvasFilters(filterExprsToGroupBy: AstFunction[] = []) {
const filtersService = useFiltersService();
const filtersService = useMemo(() => getCanvasFiltersService(), []);
const filterExpressions = useSelector(
(state: State) => filtersService.getFilters(state),
shallowEqual

View file

@ -21,11 +21,6 @@ import {
import { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public/actions';
import { trackCanvasUiMetric, METRIC_TYPE } from '../../../lib/ui_metric';
import {
useEmbeddablesService,
useUiActionsService,
useVisualizationsService,
} from '../../../services';
import { CANVAS_APP } from '../../../../common/lib';
import { ElementSpec } from '../../../../types';
import { EditorMenu as Component } from './editor_menu.component';
@ -33,6 +28,11 @@ import { embeddableInputToExpression } from '../../../../canvas_plugin_src/rende
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 {
embeddableService,
uiActionsService,
visualizationsService,
} from '../../../services/kibana_services';
interface Props {
/**
@ -47,18 +47,15 @@ interface UnwrappedEmbeddableFactory {
}
export const EditorMenu: FC<Props> = ({ addElement }) => {
const embeddablesService = useEmbeddablesService();
const { pathname, search, hash } = useLocation();
const stateTransferService = embeddablesService.getStateTransfer();
const visualizationsService = useVisualizationsService();
const uiActions = useUiActionsService();
const stateTransferService = embeddableService.getStateTransfer();
const canvasApi = useCanvasApi();
const [addPanelActions, setAddPanelActions] = useState<Array<Action<object>>>([]);
const embeddableFactories = useMemo(
() => (embeddablesService ? Array.from(embeddablesService.getEmbeddableFactories()) : []),
[embeddablesService]
() => (embeddableService ? Array.from(embeddableService.getEmbeddableFactories()) : []),
[]
);
const [unwrappedEmbeddableFactories, setUnwrappedEmbeddableFactories] = useState<
@ -79,7 +76,7 @@ export const EditorMenu: FC<Props> = ({ addElement }) => {
useEffect(() => {
let mounted = true;
async function loadPanelActions() {
const registeredActions = await uiActions?.getTriggerCompatibleActions?.(
const registeredActions = await uiActionsService.getTriggerCompatibleActions(
ADD_CANVAS_ELEMENT_TRIGGER,
{ embeddable: canvasApi }
);
@ -89,7 +86,7 @@ export const EditorMenu: FC<Props> = ({ addElement }) => {
return () => {
mounted = false;
};
}, [uiActions, canvasApi]);
}, [canvasApi]);
const createNewVisType = useCallback(
(visType?: BaseVisType | VisTypeAlias) => () => {

View file

@ -5,13 +5,14 @@
* 2.0.
*/
import React, { useState } from 'react';
import { EuiButtonEmpty, EuiNotificationBadge } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { LazyLabsFlyout, withSuspense } from '@kbn/presentation-util-plugin/public';
import { useLabsService } from '../../../services';
import { UI_SETTINGS } from '../../../../common';
import { coreServices, presentationUtilService } from '../../../services/kibana_services';
const strings = {
getLabsButtonLabel: () =>
@ -23,14 +24,13 @@ const strings = {
const Flyout = withSuspense(LazyLabsFlyout, null);
export const LabsControl = () => {
const { isLabsEnabled, getProjects } = useLabsService();
const [isShown, setIsShown] = useState(false);
if (!isLabsEnabled()) {
if (!coreServices.uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI)) {
return null;
}
const projects = getProjects(['canvas']);
const projects = presentationUtilService.labsService.getProjects(['canvas']);
const overrideCount = Object.values(projects).filter(
(project) => project.status.isOverride
).length;

View file

@ -5,14 +5,16 @@
* 2.0.
*/
import { useCallback } from 'react';
import fileSaver from 'file-saver';
import { i18n } from '@kbn/i18n';
import fileSaver from 'file-saver';
import { useCallback } from 'react';
import { API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD } from '../../../../../../common/lib/constants';
import { ZIP } from '../../../../../../i18n/constants';
import { usePlatformService, useNotifyService, useWorkpadService } from '../../../../../services';
import type { CanvasRenderedWorkpad } from '../../../../../../shareable_runtime/types';
import { useNotifyService } from '../../../../../services';
import { coreServices } from '../../../../../services/kibana_services';
import { getCanvasWorkpadService } from '../../../../../services/canvas_workpad_service';
const strings = {
getDownloadRuntimeFailureErrorMessage: () =>
@ -35,28 +37,28 @@ const strings = {
};
export const useDownloadRuntime = () => {
const platformService = usePlatformService();
const notifyService = useNotifyService();
const downloadRuntime = useCallback(() => {
try {
const path = `${platformService.getBasePath()}${API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD}`;
const path = `${coreServices.http.basePath.get()}${API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD}`;
window.open(path);
return;
} catch (err) {
notifyService.error(err, { title: strings.getDownloadRuntimeFailureErrorMessage() });
}
}, [platformService, notifyService]);
}, [notifyService]);
return downloadRuntime;
};
export const useDownloadZippedRuntime = () => {
const workpadService = useWorkpadService();
const notifyService = useNotifyService();
const downloadZippedRuntime = useCallback(
(workpad: CanvasRenderedWorkpad) => {
const workpadService = getCanvasWorkpadService();
const downloadZip = async () => {
try {
let runtimeZipBlob: Blob | undefined;
@ -80,7 +82,7 @@ export const useDownloadZippedRuntime = () => {
downloadZip();
},
[notifyService, workpadService]
[notifyService]
);
return downloadZippedRuntime;
};

View file

@ -9,11 +9,11 @@ import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { State } from '../../../../types';
import { useReportingService, usePlatformService } from '../../../services';
import { getPages, getWorkpad } from '../../../state/selectors/workpad';
import { useDownloadWorkpad } from '../../hooks';
import { ShareMenu as ShareMenuComponent } from './share_menu.component';
import { getPdfJobParams } from './utils';
import { kibanaVersion, reportingService } from '../../../services/kibana_services';
const strings = {
getUnknownExportErrorMessage: (type: string) =>
@ -27,32 +27,30 @@ const strings = {
export const ShareMenu = () => {
const downloadWorkpad = useDownloadWorkpad();
const reportingService = useReportingService();
const platformService = usePlatformService();
const { workpad, pageCount } = useSelector((state: State) => ({
workpad: getWorkpad(state),
pageCount: getPages(state).length,
}));
const ReportingPanelPDFComponent = reportingService.getReportingPanelPDFComponent();
const sharingData = {
workpad,
pageCount,
};
const ReportingComponent =
ReportingPanelPDFComponent !== null
? ({ onClose }: { onClose: () => void }) => (
<ReportingPanelPDFComponent
getJobParams={() => getPdfJobParams(sharingData, platformService.getKibanaVersion())}
const ReportingComponent = reportingService
? ({ onClose }: { onClose: () => void }) => {
const ReportingPanelPDFV2 = reportingService!.components.ReportingPanelPDFV2;
return (
<ReportingPanelPDFV2
getJobParams={() => getPdfJobParams(sharingData, kibanaVersion)}
layoutOption="canvas"
onClose={onClose}
objectId={workpad.id}
/>
)
: null;
);
}
: null;
const onExport = useCallback(
(type: string) => {

View file

@ -7,10 +7,11 @@
import { fromExpression } from '@kbn/interpreter';
import { get } from 'lodash';
import { pluginServices } from '../services';
import type { FiltersFunction } from '../../common/functions';
import { buildFiltersFunction } from '../../common/functions';
import { InitializeArguments } from '.';
import { getCanvasFiltersService } from '../services/canvas_filters_service';
import { getCanvasExpressionService } from '../services/canvas_expressions_service';
export interface Arguments {
group: string[];
@ -40,7 +41,8 @@ function getFiltersByGroup(allFilters: string[], groups?: string[], ungrouped =
export function filtersFunctionFactory(initialize: InitializeArguments): () => FiltersFunction {
const fn: FiltersFunction['fn'] = (input, { group, ungrouped }) => {
const { expressions, filters: filtersService } = pluginServices.getServices();
const expressions = getCanvasExpressionService();
const filtersService = getCanvasFiltersService();
const filterList = getFiltersByGroup(filtersService.getFilters(), group, ungrouped);

View file

@ -6,8 +6,8 @@
*/
import { i18n } from '@kbn/i18n';
import { AssetType, CanvasAsset } from '../../types';
import { CanvasNotifyService } from '../services/notify';
import { getId } from './get_id';
import { getCanvasNotifyService } from '../services/canvas_notify_service';
const strings = {
getSaveFailureTitle: () =>
@ -32,7 +32,8 @@ export const createAsset = (type: AssetType['type'], content: AssetType['value']
'@created': new Date().toISOString(),
});
export const notifyError = (err: any, notifyErrorFn: CanvasNotifyService['error']) => {
export const notifyError = (err: any) => {
const { error: notifyErrorFn } = getCanvasNotifyService();
const statusCode = err.response && err.response.status;
switch (statusCode) {
case 400:

View file

@ -12,8 +12,10 @@ import {
} from '@kbn/expressions-plugin/public';
import { updateEmbeddableExpression, fetchEmbeddableRenderable } from '../state/actions/embeddable';
import { RendererHandlers, CanvasElement } from '../../types';
import { pluginServices } from '../services';
import { getCanvasFiltersService } from '../services/canvas_filters_service';
import { clearValue } from '../state/actions/resolved_args';
// @ts-expect-error unconverted file
import { fetchAllRenderables } from '../state/actions/elements';
// This class creates stub handlers to ensure every element and renderer fulfills the contract.
// TODO: consider warning if these methods are invoked but not implemented by the renderer...?
@ -80,7 +82,7 @@ export const createDispatchedHandlerFactory = (
oldElement = element;
}
const { filters } = pluginServices.getServices();
const filters = getCanvasFiltersService();
const handlers: RendererHandlers & {
event: IInterpreterRenderHandlers['event'];
@ -94,6 +96,7 @@ export const createDispatchedHandlerFactory = (
break;
case 'applyFilterAction':
filters.updateFilter(element.id, event.data);
dispatch(fetchAllRenderables());
break;
case 'onComplete':
this.onComplete(event.data);

View file

@ -0,0 +1,26 @@
/*
* 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 { dataViewsService } from '../services/kibana_services';
import { getCanvasNotifyService } from '../services/canvas_notify_service';
import { ErrorStrings } from '../../i18n';
export const getDataViews = async () => {
try {
return await dataViewsService.getIdsWithTitle();
} catch (e) {
const { esService: strings } = ErrorStrings;
getCanvasNotifyService().error(e, { title: strings.getIndicesFetchErrorMessage() });
}
return [];
};
export const getDataViewFields = async (dataViewTitle: string) => {
const dataView = await dataViewsService.create({ title: dataViewTitle });
return dataView.fields.filter((field) => !field.name.startsWith('_')).map((field) => field.name);
};

View file

@ -8,10 +8,11 @@
import { camelCase } from 'lodash';
import { getClipboardData, setClipboardData } from './clipboard';
import { cloneSubgraphs } from './clone_subgraphs';
import { pluginServices } from '../services';
import { getId } from './get_id';
import { PositionedElement } from '../../types';
import { ELEMENT_NUDGE_OFFSET, ELEMENT_SHIFT_OFFSET } from '../../common/lib/constants';
import { getCanvasNotifyService } from '../services/canvas_notify_service';
import { getCustomElementService } from '../services/canvas_custom_element_service';
const extractId = (node: { id: string }): string => node.id;
@ -71,8 +72,8 @@ export const basicHandlerCreators = {
createCustomElement:
({ selectedNodes }: Props) =>
(name = '', description = '', image = ''): void => {
const notifyService = pluginServices.getServices().notify;
const customElementService = pluginServices.getServices().customElement;
const notifyService = getCanvasNotifyService();
const customElementService = getCustomElementService();
if (selectedNodes.length) {
const content = JSON.stringify({ selectedNodes });
@ -145,7 +146,7 @@ export const clipboardHandlerCreators = {
cutNodes:
({ pageId, removeNodes, selectedNodes }: Props) =>
(): void => {
const notifyService = pluginServices.getServices().notify;
const notifyService = getCanvasNotifyService();
if (selectedNodes.length) {
setClipboardData({ selectedNodes });
@ -156,7 +157,7 @@ export const clipboardHandlerCreators = {
copyNodes:
({ selectedNodes }: Props) =>
(): void => {
const notifyService = pluginServices.getServices().notify;
const notifyService = getCanvasNotifyService();
if (selectedNodes.length) {
setClipboardData({ selectedNodes });

View file

@ -5,22 +5,21 @@
* 2.0.
*/
import { pluginServices } from '../services';
import { coreServices } from '../services/kibana_services';
export const fullscreenClass = 'canvas-isFullscreen';
export function setFullscreen(fullscreen, doc = document) {
const platformService = pluginServices.getServices().platform;
const enabled = Boolean(fullscreen);
const body = doc.querySelector('body');
const bodyClassList = body.classList;
const isFullscreen = bodyClassList.contains(fullscreenClass);
if (enabled && !isFullscreen) {
platformService.setFullscreen(false);
coreServices.chrome.setIsVisible(false);
bodyClassList.add(fullscreenClass);
} else if (!enabled && isFullscreen) {
bodyClassList.remove(fullscreenClass);
platformService.setFullscreen(true);
coreServices.chrome.setIsVisible(true);
}
}

View file

@ -9,12 +9,11 @@
import { API_ROUTE_TEMPLATES } from '../../common/lib/constants';
import { fetch } from '../../common/lib/fetch';
import { pluginServices } from '../services';
import { CanvasTemplate } from '../../types';
import { coreServices } from '../services/kibana_services';
const getApiPath = function () {
const platformService = pluginServices.getServices().platform;
const basePath = platformService.getBasePath();
const basePath = coreServices.http.basePath.get();
return `${basePath}${API_ROUTE_TEMPLATES}`;
};

View file

@ -38,6 +38,7 @@ import { initLoadingIndicator } from './lib/loading_indicator';
import { getPluginApi, CanvasApi } from './plugin_api';
import { setupExpressions } from './setup_expressions';
import { addCanvasElementTrigger } from './state/triggers/add_canvas_element_trigger';
import { setKibanaServices, untilPluginStartServicesReady } from './services/kibana_services';
export type { CoreStart, CoreSetup };
@ -121,22 +122,13 @@ export class CanvasPlugin
setupExpressions({ coreSetup, setupPlugins });
// Get start services
const [coreStart, startPlugins] = await coreSetup.getStartServices();
const [[coreStart, startPlugins]] = await Promise.all([
coreSetup.getStartServices(),
untilPluginStartServicesReady(),
]);
srcPlugin.start(coreStart, startPlugins);
const { pluginServices } = await import('./services');
const { pluginServiceRegistry } = await import('./services/kibana');
pluginServices.setRegistry(
pluginServiceRegistry.start({
coreStart,
startPlugins,
appUpdater: this.appUpdater,
initContext: this.initContext,
})
);
const { expressions, presentationUtil } = startPlugins;
await presentationUtil.registerExpressionsLanguage(
Object.values(expressions.getFunctions())
@ -154,7 +146,13 @@ export class CanvasPlugin
this.appUpdater
);
const unmount = renderApp({ coreStart, startPlugins, params, canvasStore, pluginServices });
const unmount = renderApp({
coreStart,
startPlugins,
params,
canvasStore,
appUpdater: this.appUpdater,
});
return () => {
unmount();
@ -190,6 +188,7 @@ export class CanvasPlugin
}
public start(coreStart: CoreStart, startPlugins: CanvasStartDeps) {
setKibanaServices(coreStart, startPlugins, this.initContext);
initLoadingIndicator(coreStart.http.addLoadingCountSource);
}
}

View file

@ -6,32 +6,30 @@
*/
import { useContext, useEffect } from 'react';
import useEffectOnce from 'react-use/lib/useEffectOnce';
import { usePlatformService } from '../../../services';
import { WorkpadRoutingContext } from '..';
import { coreServices } from '../../../services/kibana_services';
const fullscreenClass = 'canvas-isFullscreen';
export const useFullscreenPresentationHelper = () => {
const { isFullscreen } = useContext(WorkpadRoutingContext);
const { setFullscreen } = usePlatformService();
useEffect(() => {
const body = document.querySelector('body');
const bodyClassList = body!.classList;
const hasFullscreenClass = bodyClassList.contains(fullscreenClass);
if (isFullscreen && !hasFullscreenClass) {
setFullscreen(false);
coreServices.chrome.setIsVisible(false);
bodyClassList.add(fullscreenClass);
} else if (!isFullscreen && hasFullscreenClass) {
bodyClassList.remove(fullscreenClass);
setFullscreen(true);
coreServices.chrome.setIsVisible(true);
}
}, [isFullscreen, setFullscreen]);
}, [isFullscreen]);
// Remove fullscreen when component unmounts
useEffectOnce(() => () => {
setFullscreen(true);
coreServices.chrome.setIsVisible(true);
document.querySelector('body')?.classList.remove(fullscreenClass);
});
};

View file

@ -7,6 +7,7 @@
import { renderHook } from '@testing-library/react-hooks';
import { useWorkpad } from './use_workpad';
import { spacesService } from '../../../services/kibana_services';
const mockDispatch = jest.fn();
const mockSelector = jest.fn();
@ -25,21 +26,22 @@ const workpadResponse = {
assets,
};
// Mock the hooks and actions used by the UseWorkpad hook
// Mock the hooks, actions, and services used by the UseWorkpad hook
jest.mock('react-redux', () => ({
useDispatch: () => mockDispatch,
useSelector: () => mockSelector,
}));
jest.mock('../../../services', () => ({
useWorkpadService: () => ({
resolve: mockResolveWorkpad,
}),
usePlatformService: () => ({
redirectLegacyUrl: mockRedirectLegacyUrl,
}),
jest.mock('../../../services/canvas_workpad_service', () => ({
getCanvasWorkpadService: () => {
return {
resolve: mockResolveWorkpad,
};
},
}));
spacesService!.ui.redirectLegacyUrl = mockRedirectLegacyUrl;
jest.mock('../../../state/actions/workpad', () => ({
setWorkpad: (payload: any) => ({
type: 'setWorkpad',

View file

@ -8,7 +8,6 @@
import { useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { useDispatch, useSelector } from 'react-redux';
import { useWorkpadService, usePlatformService } from '../../../services';
import { getWorkpad } from '../../../state/selectors/workpad';
import { setWorkpad } from '../../../state/actions/workpad';
// @ts-expect-error
@ -16,7 +15,11 @@ import { setAssets } from '../../../state/actions/assets';
// @ts-expect-error
import { setZoomScale } from '../../../state/actions/transient';
import { CanvasWorkpad } from '../../../../types';
import type { ResolveWorkpadResponse } from '../../../services/workpad';
import {
ResolveWorkpadResponse,
getCanvasWorkpadService,
} from '../../../services/canvas_workpad_service';
import { spacesService } from '../../../services/kibana_services';
const getWorkpadLabel = () =>
i18n.translate('xpack.canvas.workpadResolve.redirectLabel', {
@ -32,9 +35,6 @@ export const useWorkpad = (
loadPages: boolean = true,
getRedirectPath: (workpadId: string) => string
): [CanvasWorkpad | undefined, string | Error | undefined] => {
const workpadService = useWorkpadService();
const workpadResolve = workpadService.resolve;
const platformService = usePlatformService();
const dispatch = useDispatch();
const storedWorkpad = useSelector(getWorkpad);
const [error, setError] = useState<string | Error | undefined>(undefined);
@ -47,14 +47,12 @@ export const useWorkpad = (
const {
workpad: { assets, ...workpad },
...resolveProps
} = await workpadResolve(workpadId);
} = await getCanvasWorkpadService().resolve(workpadId);
setResolveInfo({ id: workpadId, ...resolveProps });
// If it's an alias match, we know we are going to redirect so don't even dispatch that we got the workpad
if (storedWorkpad.id !== workpadId && resolveProps.outcome !== 'aliasMatch') {
workpad.aliasId = resolveProps.aliasId;
dispatch(setAssets(assets));
dispatch(setWorkpad(workpad, { loadPages }));
dispatch(setZoomScale(1));
@ -63,7 +61,7 @@ export const useWorkpad = (
setError(e as Error | string);
}
})();
}, [workpadId, dispatch, setError, loadPages, workpadResolve, storedWorkpad.id]);
}, [workpadId, dispatch, setError, loadPages, storedWorkpad.id]);
useEffect(() => {
// If the resolved info is not for the current workpad id, bail out
@ -75,16 +73,16 @@ export const useWorkpad = (
if (!resolveInfo) return;
const { aliasId, outcome, aliasPurpose } = resolveInfo;
if (outcome === 'aliasMatch' && platformService.redirectLegacyUrl && aliasId) {
if (outcome === 'aliasMatch' && spacesService && aliasId) {
const redirectPath = getRedirectPath(aliasId);
await platformService.redirectLegacyUrl({
await spacesService.ui.redirectLegacyUrl({
path: `#${redirectPath}`,
aliasPurpose,
objectNoun: getWorkpadLabel(),
});
}
})();
}, [workpadId, resolveInfo, getRedirectPath, platformService]);
}, [workpadId, resolveInfo, getRedirectPath]);
return [storedWorkpad.id === workpadId ? storedWorkpad : undefined, error];
};

View file

@ -19,12 +19,17 @@ jest.mock('react-redux', () => ({
useSelector: (selector: any) => selector(mockGetState()),
}));
jest.mock('../../../services/canvas_workpad_service', () => ({
getCanvasWorkpadService: () => {
return {
updateWorkpad: mockUpdateWorkpad,
updateAssets: mockUpdateAssets,
update: mockUpdate,
};
},
}));
jest.mock('../../../services', () => ({
useWorkpadService: () => ({
updateWorkpad: mockUpdateWorkpad,
updateAssets: mockUpdateAssets,
update: mockUpdate,
}),
useNotifyService: () => ({
error: mockNotifyError,
}),

View file

@ -10,13 +10,10 @@ import { useSelector } from 'react-redux';
import { CanvasWorkpad, State } from '../../../../types';
import { getWorkpad } from '../../../state/selectors/workpad';
import { canUserWrite } from '../../../state/selectors/app';
import { useWorkpadService, useNotifyService } from '../../../services';
import { notifyError } from '../../../lib/assets';
import { getCanvasWorkpadService } from '../../../services/canvas_workpad_service';
export const useWorkpadPersist = () => {
const service = useWorkpadService();
const notifyService = useNotifyService();
// Watch for workpad state and then persist those changes
const [workpad, canWrite]: [CanvasWorkpad, boolean] = useSelector((state: State) => [
getWorkpad(state),
@ -30,10 +27,12 @@ export const useWorkpadPersist = () => {
useEffect(() => {
if (canWrite) {
if (workpadChanged) {
service.updateWorkpad(workpad.id, workpad).catch((err) => {
notifyError(err, notifyService.error);
});
getCanvasWorkpadService()
.updateWorkpad(workpad.id, workpad)
.catch((err) => {
notifyError(err);
});
}
}
}, [service, workpad, workpadChanged, canWrite, notifyService.error]);
}, [workpad, workpadChanged, canWrite]);
};

View file

@ -14,7 +14,7 @@ import { getWorkpad } from '../../state/selectors/workpad';
import { useFullscreenPresentationHelper } from './hooks/use_fullscreen_presentation_helper';
import { useAutoplayHelper } from './hooks/use_autoplay_helper';
import { useRefreshHelper } from './hooks/use_refresh_helper';
import { usePlatformService } from '../../services';
import { coreServices, spacesService } from '../../services/kibana_services';
const getWorkpadLabel = () =>
i18n.translate('xpack.canvas.workpadConflict.redirectLabel', {
@ -22,7 +22,6 @@ const getWorkpadLabel = () =>
});
export const WorkpadPresentationHelper: FC<PropsWithChildren<unknown>> = ({ children }) => {
const platformService = usePlatformService();
const workpad = useSelector(getWorkpad);
useFullscreenPresentationHelper();
useAutoplayHelper();
@ -30,18 +29,18 @@ export const WorkpadPresentationHelper: FC<PropsWithChildren<unknown>> = ({ chil
const history = useHistory();
useEffect(() => {
platformService.setBreadcrumbs([
coreServices.chrome.setBreadcrumbs([
getBaseBreadcrumb(history),
getWorkpadBreadcrumb({ name: workpad.name }),
]);
}, [workpad.name, platformService, history]);
}, [workpad.name, history]);
useEffect(() => {
setDocTitle(workpad.name || getUntitledWorkpadLabel());
}, [workpad.name, workpad.id]);
const conflictElement = workpad.aliasId
? platformService.getLegacyUrlConflict?.({
? spacesService?.ui.components.getLegacyUrlConflict({
objectNoun: getWorkpadLabel(),
currentObjectId: workpad.id,
otherObjectId: workpad.aliasId,

View file

@ -0,0 +1,62 @@
/*
* 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 { API_ROUTE_CUSTOM_ELEMENT } from '../../common/lib';
import { CustomElement } from '../../types';
import { coreServices } from './kibana_services';
export interface CustomElementFindResponse {
total: number;
customElements: CustomElement[];
}
class CanvasCustomElementService {
public apiPath = `${API_ROUTE_CUSTOM_ELEMENT}`;
public async create(customElement: CustomElement) {
await coreServices.http.post(this.apiPath, {
body: JSON.stringify(customElement),
version: '1',
});
}
public async get(customElementId: string): Promise<CustomElement> {
return await coreServices.http
.get<{ data: CustomElement }>(`${this.apiPath}/${customElementId}`, { version: '1' })
.then(({ data: element }) => element);
}
public async update(id: string, element: Partial<CustomElement>) {
await coreServices.http.put(`${this.apiPath}/${id}`, {
body: JSON.stringify(element),
version: '1',
});
}
public async remove(id: string) {
await coreServices.http.delete(`${this.apiPath}/${id}`, { version: '1' });
}
public async find(searchTerm: string): Promise<CustomElementFindResponse> {
return await coreServices.http.get(`${this.apiPath}/find`, {
query: {
name: searchTerm,
perPage: 10000,
},
version: '1',
});
}
}
let canvasCustomElementService: CanvasCustomElementService;
export const getCustomElementService = () => {
if (!canvasCustomElementService) {
canvasCustomElementService = new CanvasCustomElementService();
}
return canvasCustomElementService;
};

View file

@ -4,34 +4,29 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { fromExpression, getType } from '@kbn/interpreter';
import {
ExpressionAstExpression,
ExpressionExecutionParams,
ExpressionValue,
} from '@kbn/expressions-plugin/common';
import { fromExpression, getType } from '@kbn/interpreter';
import { pluck } from 'rxjs';
import { ExpressionsServiceStart } from '@kbn/expressions-plugin/public';
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { buildEmbeddableFilters } from '../../../common/lib/build_embeddable_filters';
import { CanvasStartDeps } from '../../plugin';
import { CanvasFiltersService } from './filters';
import { CanvasNotifyService } from '../notify';
import { buildEmbeddableFilters } from '../../common/lib/build_embeddable_filters';
import { expressionsService } from './kibana_services';
import { getCanvasNotifyService } from './canvas_notify_service';
import { getCanvasFiltersService } from './canvas_filters_service';
interface Options {
castToRender?: boolean;
}
export class ExpressionsService {
private filters: CanvasFiltersService;
private notify: CanvasNotifyService;
class ExpressionsService {
private notifyService;
private filtersService;
constructor(
private readonly expressions: ExpressionsServiceStart,
{ filters, notify }: CanvasExpressionsServiceRequiredServices
) {
this.filters = filters;
this.notify = notify;
constructor() {
this.notifyService = getCanvasNotifyService();
this.filtersService = getCanvasFiltersService();
}
async interpretAst(
@ -51,7 +46,7 @@ export class ExpressionsService {
input: ExpressionValue = null,
context?: ExpressionExecutionParams
): Promise<ExpressionValue> {
return await this.expressions
return await expressionsService
.execute(ast, input, { ...context, namespace: 'canvas' })
.getData()
.pipe(pluck('result'))
@ -92,22 +87,22 @@ export class ExpressionsService {
throw new Error(`Ack! I don't know how to render a '${getType(renderable)}'`);
} catch (err) {
this.notify.error(err);
this.notifyService.error(err);
throw err;
}
}
getRenderer(name: string) {
return this.expressions.getRenderer(name);
return expressionsService.getRenderer(name);
}
getFunctions() {
return this.expressions.getFunctions();
return expressionsService.getFunctions();
}
private async getFilters() {
const filtersList = this.filters.getFilters();
const context = this.filters.getFiltersContext();
const filtersList = this.filtersService.getFilters();
const context = this.filtersService.getFiltersContext();
const filterExpression = filtersList.join(' | ');
const filterAST = fromExpression(filterExpression);
return await this.interpretAstWithContext(filterAST, null, context);
@ -122,19 +117,11 @@ export class ExpressionsService {
}
}
export type CanvasExpressionsService = ExpressionsService;
export interface CanvasExpressionsServiceRequiredServices {
notify: CanvasNotifyService;
filters: CanvasFiltersService;
}
let canvasExpressionsService: ExpressionsService;
export type CanvasExpressionsServiceFactory = KibanaPluginServiceFactory<
CanvasExpressionsService,
CanvasStartDeps,
CanvasExpressionsServiceRequiredServices
>;
export const expressionsServiceFactory: CanvasExpressionsServiceFactory = (
{ startPlugins },
requiredServices
) => new ExpressionsService(startPlugins.expressions, requiredServices);
export const getCanvasExpressionService = () => {
if (!canvasExpressionsService) {
canvasExpressionsService = new ExpressionsService();
}
return canvasExpressionsService;
};

View file

@ -5,25 +5,21 @@
* 2.0.
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
// @ts-expect-error untyped local
import { getState, getStore } from '../../state/store';
import { State } from '../../../types';
import { getGlobalFilters, getWorkpadVariablesAsObject } from '../../state/selectors/workpad';
import { CanvasStartDeps } from '../../plugin';
import { getState, getStore } from '../state/store';
import { State } from '../../types';
import { getGlobalFilters, getWorkpadVariablesAsObject } from '../state/selectors/workpad';
// @ts-expect-error untyped local
import { setFilter } from '../../state/actions/elements';
export class FiltersService {
constructor() {}
import { setFilter } from '../state/actions/filters';
class FiltersService {
getFilters(state: State = getState()) {
return getGlobalFilters(state);
}
updateFilter(filterId: string, filterExpression: string) {
const { dispatch } = getStore();
dispatch(setFilter(filterExpression, filterId, true));
dispatch(setFilter(filterExpression, filterId));
}
getFiltersContext(state: State = getState()) {
@ -32,11 +28,11 @@ export class FiltersService {
}
}
export type CanvasFiltersService = FiltersService;
let canvasFiltersService: FiltersService;
export type CanvasFiltersServiceFactory = KibanaPluginServiceFactory<
CanvasFiltersService,
CanvasStartDeps
>;
export const filtersServiceFactory: CanvasFiltersServiceFactory = () => new FiltersService();
export const getCanvasFiltersService = () => {
if (!canvasFiltersService) {
canvasFiltersService = new FiltersService();
}
return canvasFiltersService;
};

View file

@ -6,17 +6,10 @@
*/
import { get } from 'lodash';
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ToastInputFields } from '@kbn/core/public';
import { formatMsg } from '../../lib/format_msg';
import { CanvasStartDeps } from '../../plugin';
import { CanvasNotifyService } from '../notify';
export type CanvasNotifyServiceFactory = KibanaPluginServiceFactory<
CanvasNotifyService,
CanvasStartDeps
>;
import { formatMsg } from '../lib/format_msg';
import { coreServices } from './kibana_services';
const getToast = (err: Error | string, opts: ToastInputFields = {}) => {
const errData = (get(err, 'response') || err) as Error | string;
@ -36,8 +29,15 @@ const getToast = (err: Error | string, opts: ToastInputFields = {}) => {
};
};
export const notifyServiceFactory: CanvasNotifyServiceFactory = ({ coreStart }) => {
const toasts = coreStart.notifications.toasts;
export interface CanvasNotifyService {
error: (err: string | Error, opts?: ToastInputFields) => void;
warning: (err: string | Error, opts?: ToastInputFields) => void;
info: (err: string | Error, opts?: ToastInputFields) => void;
success: (err: string | Error, opts?: ToastInputFields) => void;
}
export const getCanvasNotifyService = (): CanvasNotifyService => {
const toasts = coreServices.notifications.toasts;
return {
/*

View file

@ -0,0 +1,200 @@
/*
* 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 { ResolvedSimpleSavedObject, SavedObject } from '@kbn/core/public';
import {
API_ROUTE_SHAREABLE_ZIP,
API_ROUTE_TEMPLATES,
API_ROUTE_WORKPAD,
API_ROUTE_WORKPAD_ASSETS,
API_ROUTE_WORKPAD_STRUCTURES,
DEFAULT_WORKPAD_CSS,
} from '../../common/lib';
import type { CanvasRenderedWorkpad } from '../../shareable_runtime/types';
import { CanvasTemplate, CanvasWorkpad } from '../../types';
import { coreServices } from './kibana_services';
export type FoundWorkpads = Array<Pick<CanvasWorkpad, 'name' | 'id' | '@timestamp' | '@created'>>;
export type FoundWorkpad = FoundWorkpads[number];
export interface WorkpadFindResponse {
total: number;
workpads: FoundWorkpads;
}
export interface TemplateFindResponse {
templates: CanvasTemplate[];
}
export interface ResolveWorkpadResponse {
workpad: CanvasWorkpad;
outcome: ResolvedSimpleSavedObject['outcome'];
aliasId?: ResolvedSimpleSavedObject['alias_target_id'];
aliasPurpose?: ResolvedSimpleSavedObject['alias_purpose'];
}
/*
Remove any top level keys from the workpad which will be rejected by validation
*/
const validKeys = [
'@created',
'@timestamp',
'assets',
'colors',
'css',
'variables',
'height',
'id',
'isWriteable',
'name',
'page',
'pages',
'width',
];
const sanitizeWorkpad = function (workpad: CanvasWorkpad) {
const workpadKeys = Object.keys(workpad);
for (const key of workpadKeys) {
if (!validKeys.includes(key)) {
delete (workpad as { [key: string]: any })[key];
}
}
return workpad;
};
class CanvasWorkpadService {
private apiPath = `${API_ROUTE_WORKPAD}`;
public async get(id: string): Promise<CanvasWorkpad> {
const workpad = await coreServices.http.get<any>(`${this.apiPath}/${id}`, { version: '1' });
return { css: DEFAULT_WORKPAD_CSS, variables: [], ...workpad };
}
public async export(id: string) {
const workpad = await coreServices.http.get<SavedObject<CanvasWorkpad>>(
`${this.apiPath}/export/${id}`,
{ version: '1' }
);
const { attributes } = workpad;
return {
...workpad,
attributes: {
...attributes,
css: attributes.css ?? DEFAULT_WORKPAD_CSS,
variables: attributes.variables ?? [],
},
};
}
public async resolve(id: string): Promise<ResolveWorkpadResponse> {
const { workpad, ...resolveProps } = await coreServices.http.get<ResolveWorkpadResponse>(
`${this.apiPath}/resolve/${id}`,
{ version: '1' }
);
return {
...resolveProps,
workpad: {
// @ts-ignore: Shimming legacy workpads that might not have CSS
css: DEFAULT_WORKPAD_CSS,
// @ts-ignore: Shimming legacy workpads that might not have variables
variables: [],
...workpad,
},
};
}
public async create(workpad: CanvasWorkpad): Promise<CanvasWorkpad> {
return coreServices.http.post(this.apiPath, {
body: JSON.stringify({
...sanitizeWorkpad({ ...workpad }),
assets: workpad.assets || {},
variables: workpad.variables || [],
}),
version: '1',
});
}
public async import(workpad: CanvasWorkpad): Promise<CanvasWorkpad> {
return coreServices.http.post(`${this.apiPath}/import`, {
body: JSON.stringify({
...sanitizeWorkpad({ ...workpad }),
assets: workpad.assets || {},
variables: workpad.variables || [],
}),
version: '1',
});
}
public async createFromTemplate(templateId: string): Promise<CanvasWorkpad> {
return coreServices.http.post(this.apiPath, {
body: JSON.stringify({ templateId }),
version: '1',
});
}
public async findTemplates(): Promise<TemplateFindResponse> {
return coreServices.http.get(API_ROUTE_TEMPLATES, { version: '1' });
}
public async find(searchTerm: string): Promise<WorkpadFindResponse> {
// TODO: this shouldn't be necessary. Check for usage.
const validSearchTerm = typeof searchTerm === 'string' && searchTerm.length > 0;
return coreServices.http.get(`${this.apiPath}/find`, {
query: {
perPage: 10000,
name: validSearchTerm ? searchTerm : '',
},
version: '1',
});
}
public async remove(id: string) {
coreServices.http.delete(`${this.apiPath}/${id}`, { version: '1' });
}
public async update(id: string, workpad: CanvasWorkpad) {
coreServices.http.put(`${this.apiPath}/${id}`, {
body: JSON.stringify({ ...sanitizeWorkpad({ ...workpad }) }),
version: '1',
});
}
public async updateWorkpad(id: string, workpad: CanvasWorkpad) {
coreServices.http.put(`${API_ROUTE_WORKPAD_STRUCTURES}/${id}`, {
body: JSON.stringify({ ...sanitizeWorkpad({ ...workpad }) }),
version: '1',
});
}
public async updateAssets(id: string, assets: CanvasWorkpad['assets']) {
coreServices.http.put(`${API_ROUTE_WORKPAD_ASSETS}/${id}`, {
body: JSON.stringify(assets),
version: '1',
});
}
public async getRuntimeZip(workpad: CanvasRenderedWorkpad): Promise<Blob> {
return coreServices.http.post<Blob>(API_ROUTE_SHAREABLE_ZIP, {
body: JSON.stringify(workpad),
version: '1',
});
}
}
let canvasWorkpadService: CanvasWorkpadService;
export const getCanvasWorkpadService: () => CanvasWorkpadService = () => {
if (!canvasWorkpadService) {
canvasWorkpadService = new CanvasWorkpadService();
}
return canvasWorkpadService;
};

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 { CustomElement } from '../../types';
export interface CustomElementFindResponse {
total: number;
customElements: CustomElement[];
}
export interface CanvasCustomElementService {
create: (customElement: CustomElement) => Promise<void>;
get: (customElementId: string) => Promise<CustomElement>;
update: (id: string, element: Partial<CustomElement>) => Promise<void>;
remove: (id: string) => Promise<void>;
find: (searchTerm: string) => Promise<CustomElementFindResponse>;
}

View file

@ -1,14 +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 { DataView } from '@kbn/data-views-plugin/common';
export interface CanvasDataViewsService {
getFields: (index: string) => Promise<string[]>;
getDataViews: () => Promise<Array<Pick<DataView, 'id' | 'name' | 'title'>>>;
getDefaultDataView: () => Promise<Pick<DataView, 'id' | 'name' | 'title'> | undefined>;
}

View file

@ -1,22 +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 {
EmbeddableFactory,
type EmbeddableStateTransfer,
ReactEmbeddableSavedObject,
} from '@kbn/embeddable-plugin/public';
import { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common';
export interface CanvasEmbeddablesService {
reactEmbeddableRegistryHasKey: (key: string) => boolean;
getReactEmbeddableSavedObjects: <
TSavedObjectAttributes extends FinderAttributes
>() => IterableIterator<[string, ReactEmbeddableSavedObject<TSavedObjectAttributes>]>;
getEmbeddableFactories: () => IterableIterator<EmbeddableFactory>;
getStateTransfer: () => EmbeddableStateTransfer;
}

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 type { CanvasExpressionsService } from './kibana/expressions';

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 type { CanvasFiltersService } from './kibana/filters';

View file

@ -6,55 +6,11 @@
*/
export * from './legacy';
import { useMemo } from 'react';
import { PluginServices } from '@kbn/presentation-util-plugin/public';
import { getCanvasNotifyService } from './canvas_notify_service';
import { CanvasCustomElementService } from './custom_element';
import { CanvasDataViewsService } from './data_views';
import { CanvasEmbeddablesService } from './embeddables';
import { CanvasExpressionsService } from './expressions';
import { CanvasFiltersService } from './filters';
import { CanvasLabsService } from './labs';
import { CanvasNavLinkService } from './nav_link';
import { CanvasNotifyService } from './notify';
import { CanvasPlatformService } from './platform';
import { CanvasReportingService } from './reporting';
import { CanvasVisualizationsService } from './visualizations';
import { CanvasWorkpadService } from './workpad';
import { CanvasUiActionsService } from './ui_actions';
export interface CanvasPluginServices {
customElement: CanvasCustomElementService;
dataViews: CanvasDataViewsService;
embeddables: CanvasEmbeddablesService;
expressions: CanvasExpressionsService;
filters: CanvasFiltersService;
labs: CanvasLabsService;
navLink: CanvasNavLinkService;
notify: CanvasNotifyService;
platform: CanvasPlatformService;
reporting: CanvasReportingService;
visualizations: CanvasVisualizationsService;
workpad: CanvasWorkpadService;
uiActions: CanvasUiActionsService;
}
export const pluginServices = new PluginServices<CanvasPluginServices>();
export const useCustomElementService = () =>
(() => pluginServices.getHooks().customElement.useService())();
export const useDataViewsService = () => (() => pluginServices.getHooks().dataViews.useService())();
export const useEmbeddablesService = () =>
(() => pluginServices.getHooks().embeddables.useService())();
export const useExpressionsService = () =>
(() => pluginServices.getHooks().expressions.useService())();
export const useFiltersService = () => (() => pluginServices.getHooks().filters.useService())();
export const useLabsService = () => (() => pluginServices.getHooks().labs.useService())();
export const useNavLinkService = () => (() => pluginServices.getHooks().navLink.useService())();
export const useNotifyService = () => (() => pluginServices.getHooks().notify.useService())();
export const usePlatformService = () => (() => pluginServices.getHooks().platform.useService())();
export const useReportingService = () => (() => pluginServices.getHooks().reporting.useService())();
export const useVisualizationsService = () =>
(() => pluginServices.getHooks().visualizations.useService())();
export const useWorkpadService = () => (() => pluginServices.getHooks().workpad.useService())();
export const useUiActionsService = () => (() => pluginServices.getHooks().uiActions.useService())();
export const useNotifyService = () => {
const canvasNotifyService = useMemo(() => getCanvasNotifyService(), []);
return canvasNotifyService;
};

View file

@ -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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { API_ROUTE_CUSTOM_ELEMENT } from '../../../common/lib/constants';
import { CustomElement } from '../../../types';
import { CanvasStartDeps } from '../../plugin';
import { CanvasCustomElementService } from '../custom_element';
export type CanvasCustomElementServiceFactory = KibanaPluginServiceFactory<
CanvasCustomElementService,
CanvasStartDeps
>;
export const customElementServiceFactory: CanvasCustomElementServiceFactory = ({ coreStart }) => {
const { http } = coreStart;
const apiPath = `${API_ROUTE_CUSTOM_ELEMENT}`;
return {
create: (customElement) =>
http.post(apiPath, { body: JSON.stringify(customElement), version: '1' }),
get: (customElementId) =>
http
.get<{ data: CustomElement }>(`${apiPath}/${customElementId}`, { version: '1' })
.then(({ data: element }) => element),
update: (id, element) =>
http.put(`${apiPath}/${id}`, { body: JSON.stringify(element), version: '1' }),
remove: (id) => http.delete(`${apiPath}/${id}`, { version: '1' }),
find: async (name) => {
return http.get(`${apiPath}/find`, {
query: {
name,
perPage: 10000,
},
version: '1',
});
},
};
};

View file

@ -1,50 +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 { DataView } from '@kbn/data-views-plugin/public';
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ErrorStrings } from '../../../i18n';
import { CanvasStartDeps } from '../../plugin';
import { CanvasDataViewsService } from '../data_views';
import { CanvasNotifyService } from '../notify';
const { esService: strings } = ErrorStrings;
export type DataViewsServiceFactory = KibanaPluginServiceFactory<
CanvasDataViewsService,
CanvasStartDeps,
{
notify: CanvasNotifyService;
}
>;
export const dataViewsServiceFactory: DataViewsServiceFactory = ({ startPlugins }, { notify }) => ({
getDataViews: async () => {
try {
const dataViews = await startPlugins.dataViews.getIdsWithTitle();
return dataViews.map(({ id, name, title }) => ({ id, name, title } as DataView));
} catch (e) {
notify.error(e, { title: strings.getIndicesFetchErrorMessage() });
}
return [];
},
getFields: async (dataViewTitle: string) => {
const dataView = await startPlugins.dataViews.create({ title: dataViewTitle });
return dataView.fields
.filter((field) => !field.name.startsWith('_'))
.map((field) => field.name);
},
getDefaultDataView: async () => {
const dataView = await startPlugins.dataViews.getDefaultDataView();
return dataView
? { id: dataView.id, name: dataView.name, title: dataView.getIndexPattern() }
: undefined;
},
});

View file

@ -1,22 +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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasStartDeps } from '../../plugin';
import { CanvasEmbeddablesService } from '../embeddables';
export type EmbeddablesServiceFactory = KibanaPluginServiceFactory<
CanvasEmbeddablesService,
CanvasStartDeps
>;
export const embeddablesServiceFactory: EmbeddablesServiceFactory = ({ startPlugins }) => ({
reactEmbeddableRegistryHasKey: startPlugins.embeddable.reactEmbeddableRegistryHasKey,
getReactEmbeddableSavedObjects: startPlugins.embeddable.getReactEmbeddableSavedObjects,
getEmbeddableFactories: startPlugins.embeddable.getEmbeddableFactories,
getStateTransfer: startPlugins.embeddable.getStateTransfer,
});

View file

@ -1,66 +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 {
PluginServiceProviders,
PluginServiceProvider,
PluginServiceRegistry,
KibanaPluginServiceParams,
} from '@kbn/presentation-util-plugin/public';
import { CanvasPluginServices } from '..';
import { CanvasStartDeps } from '../../plugin';
import { customElementServiceFactory } from './custom_element';
import { dataViewsServiceFactory } from './data_views';
import { embeddablesServiceFactory } from './embeddables';
import { expressionsServiceFactory } from './expressions';
import { labsServiceFactory } from './labs';
import { navLinkServiceFactory } from './nav_link';
import { notifyServiceFactory } from './notify';
import { platformServiceFactory } from './platform';
import { reportingServiceFactory } from './reporting';
import { visualizationsServiceFactory } from './visualizations';
import { workpadServiceFactory } from './workpad';
import { filtersServiceFactory } from './filters';
import { uiActionsServiceFactory } from './ui_actions';
export { customElementServiceFactory } from './custom_element';
export { dataViewsServiceFactory } from './data_views';
export { embeddablesServiceFactory } from './embeddables';
export { expressionsServiceFactory } from './expressions';
export { filtersServiceFactory } from './filters';
export { labsServiceFactory } from './labs';
export { notifyServiceFactory } from './notify';
export { platformServiceFactory } from './platform';
export { reportingServiceFactory } from './reporting';
export { visualizationsServiceFactory } from './visualizations';
export { workpadServiceFactory } from './workpad';
export { uiActionsServiceFactory } from './ui_actions';
export const pluginServiceProviders: PluginServiceProviders<
CanvasPluginServices,
KibanaPluginServiceParams<CanvasStartDeps>
> = {
customElement: new PluginServiceProvider(customElementServiceFactory),
dataViews: new PluginServiceProvider(dataViewsServiceFactory, ['notify']),
embeddables: new PluginServiceProvider(embeddablesServiceFactory),
expressions: new PluginServiceProvider(expressionsServiceFactory, ['filters', 'notify']),
filters: new PluginServiceProvider(filtersServiceFactory),
labs: new PluginServiceProvider(labsServiceFactory),
navLink: new PluginServiceProvider(navLinkServiceFactory),
notify: new PluginServiceProvider(notifyServiceFactory),
platform: new PluginServiceProvider(platformServiceFactory),
reporting: new PluginServiceProvider(reportingServiceFactory),
visualizations: new PluginServiceProvider(visualizationsServiceFactory),
workpad: new PluginServiceProvider(workpadServiceFactory),
uiActions: new PluginServiceProvider(uiActionsServiceFactory),
};
export const pluginServiceRegistry = new PluginServiceRegistry<
CanvasPluginServices,
KibanaPluginServiceParams<CanvasStartDeps>
>(pluginServiceProviders);

View file

@ -1,23 +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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { projectIDs } from '@kbn/presentation-util-plugin/common';
import { UI_SETTINGS } from '../../../common';
import { CanvasStartDeps } from '../../plugin';
import { CanvasLabsService } from '../labs';
export type CanvasLabsServiceFactory = KibanaPluginServiceFactory<
CanvasLabsService,
CanvasStartDeps
>;
export const labsServiceFactory: CanvasLabsServiceFactory = ({ startPlugins, coreStart }) => ({
projectIDs,
isLabsEnabled: () => coreStart.uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI),
...startPlugins.presentationUtil.labsService,
});

View file

@ -1,28 +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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { SESSIONSTORAGE_LASTPATH } from '../../../common/lib/constants';
import { getSessionStorage } from '../../lib/storage';
import { CanvasStartDeps } from '../../plugin';
import { CanvasNavLinkService } from '../nav_link';
export type CanvasNavLinkServiceFactory = KibanaPluginServiceFactory<
CanvasNavLinkService,
CanvasStartDeps
>;
export const navLinkServiceFactory: CanvasNavLinkServiceFactory = ({ coreStart, appUpdater }) => ({
updatePath: (path: string) => {
appUpdater?.next(() => ({
defaultPath: `${path}`,
}));
getSessionStorage().set(`${SESSIONSTORAGE_LASTPATH}:${coreStart.http.basePath.get()}`, path);
},
});

View file

@ -1,46 +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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasStartDeps } from '../../plugin';
import { CanvasPlatformService } from '../platform';
export type CanvaPlatformServiceFactory = KibanaPluginServiceFactory<
CanvasPlatformService,
CanvasStartDeps
>;
export const platformServiceFactory: CanvaPlatformServiceFactory = ({
coreStart,
initContext,
startPlugins,
}) => {
if (!initContext) {
throw new Error('Canvas platform service requires init context');
}
return {
getBasePath: coreStart.http.basePath.get,
getBasePathInterface: () => coreStart.http.basePath,
getElasticWebsiteUrl: () => coreStart.docLinks.ELASTIC_WEBSITE_URL,
getDocLinkVersion: () => coreStart.docLinks.DOC_LINK_VERSION,
getKibanaVersion: () => initContext.env.packageInfo.version,
// TODO: is there a better type for this? The capabilities type allows for a Record,
// though we don't do this. So this cast may be the best option.
getHasWriteAccess: () => coreStart.application.capabilities.canvas.save as boolean,
getUISetting: coreStart.uiSettings.get.bind(coreStart.uiSettings),
hasHeaderBanner$: coreStart.chrome.hasHeaderBanner$,
setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
setRecentlyAccessed: coreStart.chrome.recentlyAccessed.add,
setFullscreen: coreStart.chrome.setIsVisible,
redirectLegacyUrl: startPlugins.spaces?.ui.redirectLegacyUrl,
getLegacyUrlConflict: startPlugins.spaces?.ui.components.getLegacyUrlConflict,
getUISettings: () => coreStart.uiSettings,
getHttp: () => coreStart.http,
getContentManagement: () => startPlugins.contentManagement,
};
};

View file

@ -1,45 +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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasStartDeps } from '../../plugin';
import { CanvasReportingService } from '../reporting';
export type CanvasReportingServiceFactory = KibanaPluginServiceFactory<
CanvasReportingService,
CanvasStartDeps
>;
export const reportingServiceFactory: CanvasReportingServiceFactory = ({
startPlugins,
coreStart,
}) => {
const { reporting } = startPlugins;
const reportingEnabled = () => ({
getReportingPanelPDFComponent: () => reporting?.components.ReportingPanelPDFV2 || null,
});
const reportingDisabled = () => ({ getReportingPanelPDFComponent: () => null });
if (!reporting) {
// Reporting is not enabled
return reportingDisabled();
}
if (reporting.usesUiCapabilities()) {
if (coreStart.application.capabilities.canvas?.generatePdf === true) {
// Canvas has declared Reporting as a subfeature with the `generatePdf` UI Capability
return reportingEnabled();
} else {
return reportingDisabled();
}
}
// Legacy/Deprecated: Reporting is enabled as an Elasticsearch feature
return reportingEnabled();
};

View file

@ -1,19 +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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasStartDeps } from '../../plugin';
import { CanvasUiActionsService } from '../ui_actions';
export type UiActionsServiceFactory = KibanaPluginServiceFactory<
CanvasUiActionsService,
CanvasStartDeps
>;
export const uiActionsServiceFactory: UiActionsServiceFactory = ({ startPlugins }) => ({
getTriggerCompatibleActions: startPlugins.uiActions.getTriggerCompatibleActions,
});

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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasStartDeps } from '../../plugin';
import { CanvasVisualizationsService } from '../visualizations';
export type VisualizationsServiceFactory = KibanaPluginServiceFactory<
CanvasVisualizationsService,
CanvasStartDeps
>;
export const visualizationsServiceFactory: VisualizationsServiceFactory = ({ startPlugins }) => ({
showNewVisModal: startPlugins.visualizations.showNewVisModal,
getByGroup: startPlugins.visualizations.getByGroup,
getAliases: startPlugins.visualizations.getAliases,
});

View file

@ -1,170 +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 { SavedObject } from '@kbn/core/public';
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasStartDeps } from '../../plugin';
import { CanvasWorkpadService, ResolveWorkpadResponse } from '../workpad';
import {
API_ROUTE_WORKPAD,
DEFAULT_WORKPAD_CSS,
API_ROUTE_TEMPLATES,
API_ROUTE_WORKPAD_ASSETS,
API_ROUTE_WORKPAD_STRUCTURES,
API_ROUTE_SHAREABLE_ZIP,
} from '../../../common/lib/constants';
import { CanvasWorkpad } from '../../../types';
export type CanvasWorkpadServiceFactory = KibanaPluginServiceFactory<
CanvasWorkpadService,
CanvasStartDeps
>;
/*
Remove any top level keys from the workpad which will be rejected by validation
*/
const validKeys = [
'@created',
'@timestamp',
'assets',
'colors',
'css',
'variables',
'height',
'id',
'isWriteable',
'name',
'page',
'pages',
'width',
];
const sanitizeWorkpad = function (workpad: CanvasWorkpad) {
const workpadKeys = Object.keys(workpad);
for (const key of workpadKeys) {
if (!validKeys.includes(key)) {
delete (workpad as { [key: string]: any })[key];
}
}
return workpad;
};
export const workpadServiceFactory: CanvasWorkpadServiceFactory = ({ coreStart, startPlugins }) => {
const getApiPath = function () {
return `${API_ROUTE_WORKPAD}`;
};
return {
get: async (id: string) => {
const workpad = await coreStart.http.get<any>(`${getApiPath()}/${id}`, { version: '1' });
return { css: DEFAULT_WORKPAD_CSS, variables: [], ...workpad };
},
export: async (id: string) => {
const workpad = await coreStart.http.get<SavedObject<CanvasWorkpad>>(
`${getApiPath()}/export/${id}`,
{ version: '1' }
);
const { attributes } = workpad;
return {
...workpad,
attributes: {
...attributes,
css: attributes.css ?? DEFAULT_WORKPAD_CSS,
variables: attributes.variables ?? [],
},
};
},
resolve: async (id: string) => {
const { workpad, ...resolveProps } = await coreStart.http.get<ResolveWorkpadResponse>(
`${getApiPath()}/resolve/${id}`,
{ version: '1' }
);
return {
...resolveProps,
workpad: {
// @ts-ignore: Shimming legacy workpads that might not have CSS
css: DEFAULT_WORKPAD_CSS,
// @ts-ignore: Shimming legacy workpads that might not have variables
variables: [],
...workpad,
},
};
},
create: (workpad: CanvasWorkpad) => {
return coreStart.http.post(getApiPath(), {
body: JSON.stringify({
...sanitizeWorkpad({ ...workpad }),
assets: workpad.assets || {},
variables: workpad.variables || [],
}),
version: '1',
});
},
import: (workpad: CanvasWorkpad) =>
coreStart.http.post(`${getApiPath()}/import`, {
body: JSON.stringify({
...sanitizeWorkpad({ ...workpad }),
assets: workpad.assets || {},
variables: workpad.variables || [],
}),
version: '1',
}),
createFromTemplate: (templateId: string) => {
return coreStart.http.post(getApiPath(), {
body: JSON.stringify({ templateId }),
version: '1',
});
},
findTemplates: async () => coreStart.http.get(API_ROUTE_TEMPLATES, { version: '1' }),
find: (searchTerm: string) => {
// TODO: this shouldn't be necessary. Check for usage.
const validSearchTerm = typeof searchTerm === 'string' && searchTerm.length > 0;
return coreStart.http.get(`${getApiPath()}/find`, {
query: {
perPage: 10000,
name: validSearchTerm ? searchTerm : '',
},
version: '1',
});
},
remove: (id: string) => {
return coreStart.http.delete(`${getApiPath()}/${id}`, { version: '1' });
},
update: (id, workpad) => {
return coreStart.http.put(`${getApiPath()}/${id}`, {
body: JSON.stringify({ ...sanitizeWorkpad({ ...workpad }) }),
version: '1',
});
},
updateWorkpad: (id, workpad) => {
return coreStart.http.put(`${API_ROUTE_WORKPAD_STRUCTURES}/${id}`, {
body: JSON.stringify({ ...sanitizeWorkpad({ ...workpad }) }),
version: '1',
});
},
updateAssets: (id, assets) => {
return coreStart.http.put(`${API_ROUTE_WORKPAD_ASSETS}/${id}`, {
body: JSON.stringify(assets),
version: '1',
});
},
getRuntimeZip: (workpad) => {
return coreStart.http.post<Blob>(API_ROUTE_SHAREABLE_ZIP, {
body: JSON.stringify(workpad),
version: '1',
});
},
};
};

View file

@ -0,0 +1,76 @@
/*
* 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 { BehaviorSubject } from 'rxjs';
import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
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 { ExpressionsStart } from '@kbn/expressions-plugin/public';
import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public';
import type { ReportingStart } from '@kbn/reporting-plugin/public';
import type { SpacesApi } from '@kbn/spaces-plugin/public';
import type { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin';
import type { VisualizationsStart } from '@kbn/visualizations-plugin/public';
import type { CanvasStartDeps } from '../plugin';
export let kibanaVersion: string;
export let coreServices: CoreStart;
export let contentManagementService: ContentManagementPublicStart;
export let dataService: DataPublicPluginStart;
export let dataViewsService: DataViewsPublicPluginStart;
export let embeddableService: EmbeddableStart;
export let expressionsService: ExpressionsStart;
export let presentationUtilService: PresentationUtilPluginStart;
export let reportingService: ReportingStart | undefined;
export let spacesService: SpacesApi | undefined;
export let uiActionsService: UiActionsPublicStart;
export let visualizationsService: VisualizationsStart;
const servicesReady$ = new BehaviorSubject(false);
export const setKibanaServices = (
kibanaCore: CoreStart,
deps: CanvasStartDeps,
initContext: PluginInitializerContext
) => {
kibanaVersion = initContext.env.packageInfo.version;
coreServices = kibanaCore;
contentManagementService = deps.contentManagement;
dataService = deps.data;
dataViewsService = deps.dataViews;
embeddableService = deps.embeddable;
expressionsService = deps.expressions;
presentationUtilService = deps.presentationUtil;
reportingService = Boolean(
deps.reporting?.usesUiCapabilities() && !kibanaCore.application.capabilities.canvas?.generatePdf
)
? undefined
: deps.reporting;
spacesService = deps.spaces;
uiActionsService = deps.uiActions;
visualizationsService = deps.visualizations;
servicesReady$.next(true);
};
export const untilPluginStartServicesReady = () => {
if (servicesReady$.value) return Promise.resolve();
return new Promise<void>((resolve) => {
const subscription = servicesReady$.subscribe((isInitialized) => {
if (isInitialized) {
subscription.unsubscribe();
resolve();
}
});
});
};

View file

@ -1,14 +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 { PresentationLabsService } from '@kbn/presentation-util-plugin/public';
import { projectIDs } from '@kbn/presentation-util-plugin/common';
export interface CanvasLabsService extends PresentationLabsService {
projectIDs: typeof projectIDs;
isLabsEnabled: () => boolean;
}

View file

@ -1,42 +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 { ReportingStart } from '@kbn/reporting-plugin/public';
import { CanvasServiceFactory } from '.';
export interface ReportingService {
start?: ReportingStart;
}
export const reportingServiceFactory: CanvasServiceFactory<ReportingService> = (
_coreSetup,
coreStart,
_setupPlugins,
startPlugins
): ReportingService => {
const { reporting } = startPlugins;
const reportingEnabled = () => ({ start: reporting });
const reportingDisabled = () => ({ start: undefined });
if (!reporting) {
// Reporting is not enabled
return reportingDisabled();
}
if (reporting.usesUiCapabilities()) {
if (coreStart.application.capabilities.canvas?.generatePdf === true) {
// Canvas has declared Reporting as a subfeature with the `generatePdf` UI Capability
return reportingEnabled();
} else {
return reportingDisabled();
}
}
// Legacy/Deprecated: Reporting is enabled as an Elasticsearch feature
return reportingEnabled();
};

View file

@ -0,0 +1,125 @@
/*
* 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 moment from 'moment';
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks';
import { CoreStart } from '@kbn/core/public';
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks';
import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks';
import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks';
import { presentationUtilPluginMock } from '@kbn/presentation-util-plugin/public/mocks';
import { reportingPluginMock } from '@kbn/reporting-plugin/public/mocks';
import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks';
import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks';
import { visualizationsPluginMock } from '@kbn/visualizations-plugin/public/mocks';
import { setKibanaServices } from './kibana_services';
import { getId } from '../lib/get_id';
// @ts-expect-error
import { getDefaultWorkpad } from '../state/defaults';
const setDefaultPresentationUtilCapabilities = (core: CoreStart) => {
core.application.capabilities = {
...core.application.capabilities,
dashboard: {
show: true,
createNew: true,
},
visualize: {
save: true,
},
advancedSettings: {
save: true,
},
};
};
export const setStubKibanaServices = () => {
const core: CoreStart = coreMock.createStart();
setDefaultPresentationUtilCapabilities(core);
setKibanaServices(
core,
{
charts: chartPluginMock.createStartContract(),
contentManagement: contentManagementMock.createStartContract(),
data: dataPluginMock.createStartContract(),
dataViews: dataViewPluginMocks.createStartContract(),
embeddable: embeddablePluginMock.createStartContract(),
expressions: expressionsPluginMock.createStartContract(),
inspector: inspectorPluginMock.createStartContract(),
presentationUtil: presentationUtilPluginMock.createStartContract(),
reporting: reportingPluginMock.createStartContract(),
spaces: spacesPluginMock.createStartContract(),
uiActions: uiActionsPluginMock.createStartContract(),
visualizations: visualizationsPluginMock.createStartContract(),
},
coreMock.createPluginInitializerContext()
);
};
const TIMEOUT = 500;
export const getSomeWorkpads = (count = 3, useStaticData = false) => {
if (useStaticData) {
const DAY = 86400000;
const JAN_1_2000 = 946684800000;
const workpads = [];
for (let i = 0; i < count; i++) {
workpads[i] = {
...getDefaultWorkpad(),
name: `Workpad ${i}`,
id: `workpad-${i}`,
'@created': moment(JAN_1_2000 + DAY * i).toDate(),
'@timestamp': moment(JAN_1_2000 + DAY * (i + 1)).toDate(),
};
}
return workpads;
} else {
return Array.from({ length: count }, () => ({
'@created': getRandomDate(
moment().subtract(3, 'days').toDate(),
moment().subtract(10, 'days').toDate()
),
'@timestamp': getRandomDate(),
id: getId('workpad'),
name: getRandomName(),
}));
}
};
export const findSomeWorkpads =
(count = 3, useStaticData = false, timeout = TIMEOUT) =>
(_term: string) => {
return Promise.resolve()
.then(promiseTimeout(timeout))
.then(() => ({
total: count,
workpads: getSomeWorkpads(count, useStaticData),
}));
};
const promiseTimeout = (time: number) => () => new Promise((resolve) => setTimeout(resolve, time));
const getRandomName = () => {
const lorem =
'Lorem ipsum dolor sit amet consectetur adipiscing elit Fusce lobortis aliquet arcu ut turpis duis'.split(
' '
);
return [1, 2, 3].map(() => lorem[Math.floor(Math.random() * lorem.length)]).join(' ');
};
const getRandomDate = (
start: Date = moment().toDate(),
end: Date = moment().subtract(7, 'days').toDate()
) => new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toISOString();

View file

@ -1,15 +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 { ToastInputFields } from '@kbn/core/public';
export interface CanvasNotifyService {
error: (err: string | Error, opts?: ToastInputFields) => void;
warning: (err: string | Error, opts?: ToastInputFields) => void;
info: (err: string | Error, opts?: ToastInputFields) => void;
success: (err: string | Error, opts?: ToastInputFields) => void;
}

View file

@ -1,37 +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 { Observable } from 'rxjs';
import {
IUiSettingsClient,
ChromeBreadcrumb,
IBasePath,
ChromeStart,
HttpStart,
} from '@kbn/core/public';
import { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
export interface CanvasPlatformService {
getBasePath: () => string;
getBasePathInterface: () => IBasePath;
getDocLinkVersion: () => string;
getElasticWebsiteUrl: () => string;
getKibanaVersion: () => string;
getHasWriteAccess: () => boolean;
getUISetting: (key: string, defaultValue?: any) => any;
hasHeaderBanner$: () => Observable<boolean>;
setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void;
setRecentlyAccessed: (link: string, label: string, id: string) => void;
setFullscreen: ChromeStart['setIsVisible'];
redirectLegacyUrl?: SpacesPluginStart['ui']['redirectLegacyUrl'];
getLegacyUrlConflict?: SpacesPluginStart['ui']['components']['getLegacyUrlConflict'];
getUISettings: () => IUiSettingsClient;
getHttp: () => HttpStart;
getContentManagement: () => ContentManagementPublicStart;
}

View file

@ -1,13 +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 { ReportingStart } from '@kbn/reporting-plugin/public';
type ReportingPanelPDFComponent = ReportingStart['components']['ReportingPanelPDFV2'];
export interface CanvasReportingService {
getReportingPanelPDFComponent: () => ReportingPanelPDFComponent | null;
}

View file

@ -1,60 +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 {
PluginServiceProviders,
PluginServiceProvider,
} from '@kbn/presentation-util-plugin/public';
import { CanvasPluginServices } from '..';
import { pluginServiceProviders as stubProviders } from '../stubs';
import { workpadServiceFactory } from './workpad';
import { notifyServiceFactory } from './notify';
export interface StorybookParams {
hasTemplates?: boolean;
useStaticData?: boolean;
workpadCount?: number;
}
export const pluginServiceProviders: PluginServiceProviders<CanvasPluginServices, StorybookParams> =
{
...stubProviders,
workpad: new PluginServiceProvider(workpadServiceFactory),
notify: new PluginServiceProvider(notifyServiceFactory),
};
export const argTypes = {
hasTemplates: {
name: 'Has templates?',
type: {
name: 'boolean',
},
defaultValue: true,
control: {
type: 'boolean',
},
},
useStaticData: {
name: 'Use static data?',
type: {
name: 'boolean',
},
defaultValue: false,
control: {
type: 'boolean',
},
},
workpadCount: {
name: 'Number of workpads',
type: { name: 'number' },
defaultValue: 5,
control: {
type: 'range',
},
},
};

View file

@ -1,22 +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 { action } from '@storybook/addon-actions';
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { StorybookParams } from '.';
import { CanvasNotifyService } from '../notify';
type CanvasNotifyServiceFactory = PluginServiceFactory<CanvasNotifyService, StorybookParams>;
export const notifyServiceFactory: CanvasNotifyServiceFactory = () => ({
success: (message) => action(`success: ${message}`)(),
error: (message) => action(`error: ${message}`)(),
info: (message) => action(`info: ${message}`)(),
warning: (message) => action(`warning: ${message}`)(),
});

View file

@ -1,122 +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 moment from 'moment';
import { action } from '@storybook/addon-actions';
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { getId } from '../../lib/get_id';
// @ts-expect-error
import { getDefaultWorkpad } from '../../state/defaults';
import { StorybookParams } from '.';
import { CanvasWorkpadService } from '../workpad';
import * as stubs from '../stubs/workpad';
export {
findNoTemplates,
findNoWorkpads,
findSomeTemplates,
getNoTemplates,
getSomeTemplates,
} from '../stubs/workpad';
type CanvasWorkpadServiceFactory = PluginServiceFactory<CanvasWorkpadService, StorybookParams>;
const TIMEOUT = 500;
const promiseTimeout = (time: number) => () => new Promise((resolve) => setTimeout(resolve, time));
const { findNoTemplates, findNoWorkpads, findSomeTemplates, importWorkpad } = stubs;
const getRandomName = () => {
const lorem =
'Lorem ipsum dolor sit amet consectetur adipiscing elit Fusce lobortis aliquet arcu ut turpis duis'.split(
' '
);
return [1, 2, 3].map(() => lorem[Math.floor(Math.random() * lorem.length)]).join(' ');
};
const getRandomDate = (
start: Date = moment().toDate(),
end: Date = moment().subtract(7, 'days').toDate()
) => new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toISOString();
export const getSomeWorkpads = (count = 3) =>
Array.from({ length: count }, () => ({
'@created': getRandomDate(
moment().subtract(3, 'days').toDate(),
moment().subtract(10, 'days').toDate()
),
'@timestamp': getRandomDate(),
id: getId('workpad'),
name: getRandomName(),
}));
export const findSomeWorkpads =
(count = 3, useStaticData = false, timeout = TIMEOUT) =>
(_term: string) => {
return Promise.resolve()
.then(promiseTimeout(timeout))
.then(() => ({
total: count,
workpads: useStaticData ? stubs.getSomeWorkpads(count) : getSomeWorkpads(count),
}));
};
export const workpadServiceFactory: CanvasWorkpadServiceFactory = ({
workpadCount,
hasTemplates,
useStaticData,
}) => ({
get: (id: string) => {
action('workpadService.get')(id);
return Promise.resolve({ ...getDefaultWorkpad(), id });
},
resolve: (id: string) => {
action('workpadService.resolve')(id);
return Promise.resolve({ outcome: 'exactMatch', workpad: { ...getDefaultWorkpad(), id } });
},
findTemplates: () => {
action('workpadService.findTemplates')();
return (hasTemplates ? findSomeTemplates() : findNoTemplates())();
},
import: (workpad) => {
action('workpadService.import')(workpad);
return importWorkpad(workpad);
},
create: (workpad) => {
action('workpadService.create')(workpad);
return Promise.resolve(workpad);
},
createFromTemplate: (templateId: string) => {
action('workpadService.createFromTemplate')(templateId);
return Promise.resolve(getDefaultWorkpad());
},
find: (term: string) => {
action('workpadService.find')(term);
return (workpadCount ? findSomeWorkpads(workpadCount, useStaticData) : findNoWorkpads())(term);
},
remove: (id: string) => {
action('workpadService.remove')(id);
return Promise.resolve();
},
update: (id, workpad) => {
action('worpadService.update')(workpad, id);
return Promise.resolve();
},
updateWorkpad: (id, workpad) => {
action('workpadService.updateWorkpad')(workpad, id);
return Promise.resolve();
},
updateAssets: (id, assets) => {
action('workpadService.updateAssets')(assets, id);
return Promise.resolve();
},
getRuntimeZip: (workpad) =>
Promise.resolve(new Blob([JSON.stringify(workpad)], { type: 'application/json' })),
});

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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasCustomElementService } from '../custom_element';
type CanvasCustomElementServiceFactory = PluginServiceFactory<CanvasCustomElementService>;
const noop = (..._args: any[]): any => {};
export const customElementServiceFactory: CanvasCustomElementServiceFactory = () => ({
create: noop,
find: noop,
get: noop,
remove: noop,
update: noop,
});

View file

@ -1,26 +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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasDataViewsService } from '../data_views';
type DataViewsServiceFactory = PluginServiceFactory<CanvasDataViewsService>;
export const dataViewsServiceFactory: DataViewsServiceFactory = () => ({
getDataViews: () =>
Promise.resolve([
{ id: 'dataview1', title: 'dataview1', name: 'Data view 1' },
{ id: 'dataview2', title: 'dataview2', name: 'Data view 2' },
]),
getFields: () => Promise.resolve(['field1', 'field2']),
getDefaultDataView: () =>
Promise.resolve({
id: 'defaultDataViewId',
title: 'defaultDataView',
name: 'Default data view',
}),
});

View file

@ -1,20 +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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasEmbeddablesService } from '../embeddables';
type EmbeddablesServiceFactory = PluginServiceFactory<CanvasEmbeddablesService>;
const noop = (..._args: any[]): any => {};
export const embeddablesServiceFactory: EmbeddablesServiceFactory = () => ({
reactEmbeddableRegistryHasKey: noop,
getReactEmbeddableSavedObjects: noop,
getEmbeddableFactories: noop,
getStateTransfer: noop,
});

View file

@ -1,41 +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 { AnyExpressionRenderDefinition } from '@kbn/expressions-plugin/common';
import { plugin } from '@kbn/expressions-plugin/public';
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { functions as functionDefinitions } from '../../../canvas_plugin_src/functions/common';
import { renderFunctions } from '../../../canvas_plugin_src/renderers/core';
import {
CanvasExpressionsService,
CanvasExpressionsServiceRequiredServices,
ExpressionsService,
} from '../kibana/expressions';
type CanvasExpressionsServiceFactory = PluginServiceFactory<
CanvasExpressionsService,
{},
CanvasExpressionsServiceRequiredServices
>;
export const expressionsServiceFactory: CanvasExpressionsServiceFactory = (
params,
requiredServices
) => {
const placeholder = {} as any;
const expressionsPlugin = plugin(placeholder);
const setup = expressionsPlugin.setup(placeholder);
const fork = setup.fork('canvas');
const expressionsService = fork.setup();
functionDefinitions.forEach((fn) => expressionsService.registerFunction(fn));
renderFunctions.forEach((fn) => {
setup.registerRenderer(fn as unknown as AnyExpressionRenderDefinition);
});
return new ExpressionsService(fork.start(), requiredServices);
};

View file

@ -1,23 +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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasFiltersService } from '../filters';
export type CanvasFiltersServiceFactory = PluginServiceFactory<CanvasFiltersService, {}>;
const noop = (..._args: any[]): any => {};
export const filtersServiceFactory: CanvasFiltersServiceFactory = () => ({
getFilters: () => [
'exactly value="machine-learning" column="project1" filterGroup="Group 1"',
'exactly value="kibana" column="project2" filterGroup="Group 1"',
'time column="@timestamp1" from="2021-11-02 17:13:18" to="2021-11-09 17:13:18" filterGroup="Some group"',
],
updateFilter: noop,
getFiltersContext: () => ({ variables: {} }),
});

View file

@ -1,61 +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 '../legacy/stubs';
import {
PluginServiceProviders,
PluginServiceProvider,
PluginServiceRegistry,
} from '@kbn/presentation-util-plugin/public';
import { CanvasPluginServices } from '..';
import { customElementServiceFactory } from './custom_element';
import { dataViewsServiceFactory } from './data_views';
import { embeddablesServiceFactory } from './embeddables';
import { expressionsServiceFactory } from './expressions';
import { labsServiceFactory } from './labs';
import { navLinkServiceFactory } from './nav_link';
import { notifyServiceFactory } from './notify';
import { platformServiceFactory } from './platform';
import { reportingServiceFactory } from './reporting';
import { visualizationsServiceFactory } from './visualizations';
import { workpadServiceFactory } from './workpad';
import { filtersServiceFactory } from './filters';
import { uiActionsServiceFactory } from './ui_actions';
export { customElementServiceFactory } from './custom_element';
export { dataViewsServiceFactory } from './data_views';
export { expressionsServiceFactory } from './expressions';
export { filtersServiceFactory } from './filters';
export { labsServiceFactory } from './labs';
export { navLinkServiceFactory } from './nav_link';
export { notifyServiceFactory } from './notify';
export { platformServiceFactory } from './platform';
export { reportingServiceFactory } from './reporting';
export { visualizationsServiceFactory } from './visualizations';
export { workpadServiceFactory } from './workpad';
export const pluginServiceProviders: PluginServiceProviders<CanvasPluginServices> = {
customElement: new PluginServiceProvider(customElementServiceFactory),
dataViews: new PluginServiceProvider(dataViewsServiceFactory),
embeddables: new PluginServiceProvider(embeddablesServiceFactory),
expressions: new PluginServiceProvider(expressionsServiceFactory, ['filters', 'notify']),
filters: new PluginServiceProvider(filtersServiceFactory),
labs: new PluginServiceProvider(labsServiceFactory),
navLink: new PluginServiceProvider(navLinkServiceFactory),
notify: new PluginServiceProvider(notifyServiceFactory),
platform: new PluginServiceProvider(platformServiceFactory),
reporting: new PluginServiceProvider(reportingServiceFactory),
visualizations: new PluginServiceProvider(visualizationsServiceFactory),
workpad: new PluginServiceProvider(workpadServiceFactory),
uiActions: new PluginServiceProvider(uiActionsServiceFactory),
};
export const pluginServiceRegistry = new PluginServiceRegistry<CanvasPluginServices>(
pluginServiceProviders
);

View file

@ -1,25 +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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { projectIDs } from '@kbn/presentation-util-plugin/common';
import { CanvasLabsService } from '../labs';
type CanvasLabsServiceFactory = PluginServiceFactory<CanvasLabsService>;
const noop = (..._args: any[]): any => {};
export const labsServiceFactory: CanvasLabsServiceFactory = () => ({
getProject: noop,
getProjects: noop,
getProjectIDs: () => projectIDs,
isProjectEnabled: () => false,
isLabsEnabled: () => true,
projectIDs,
reset: noop,
setProjectStatus: noop,
});

View file

@ -1,17 +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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasNavLinkService } from '../nav_link';
type CanvasNavLinkServiceFactory = PluginServiceFactory<CanvasNavLinkService>;
const noop = (..._args: any[]): any => {};
export const navLinkServiceFactory: CanvasNavLinkServiceFactory = () => ({
updatePath: noop,
});

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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasNotifyService } from '../notify';
type CanvasNotifyServiceFactory = PluginServiceFactory<CanvasNotifyService>;
const noop = (..._args: any[]): any => {};
export const notifyServiceFactory: CanvasNotifyServiceFactory = () => ({
error: noop,
info: noop,
success: noop,
warning: noop,
});

View file

@ -1,39 +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 { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { CanvasPlatformService } from '../platform';
type CanvasPlatformServiceFactory = PluginServiceFactory<CanvasPlatformService>;
const noop = (..._args: any[]): any => {};
const uiSettings: Record<string, any> = {
dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
};
const getUISetting = (setting: string) => uiSettings[setting];
export const platformServiceFactory: CanvasPlatformServiceFactory = () => ({
getBasePath: () => '/base/path',
getBasePathInterface: noop,
getDocLinkVersion: () => 'docLinkVersion',
getElasticWebsiteUrl: () => 'https://elastic.co',
getKibanaVersion: () => 'kibanaVersion',
getHasWriteAccess: () => true,
getUISetting,
hasHeaderBanner$: noop,
setBreadcrumbs: noop,
setRecentlyAccessed: noop,
getUISettings: noop,
setFullscreen: noop,
redirectLegacyUrl: noop,
getLegacyUrlConflict: undefined,
getHttp: noop,
getContentManagement: noop,
});

Some files were not shown because too many files have changed in this diff Show more