Skip to content

Commit

Permalink
[CANVAS] Moves notify to a canvas service (elastic#63268)
Browse files Browse the repository at this point in the history
* Moves notify to a canvas service

* Typecheck fix
  • Loading branch information
Corey Robertson committed Apr 28, 2020
1 parent bee297c commit 3a4b187
Show file tree
Hide file tree
Showing 24 changed files with 287 additions and 152 deletions.
14 changes: 13 additions & 1 deletion x-pack/legacy/plugins/canvas/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ import { ACTION_VALUE_CLICK } from '../../../../../src/plugins/data/public/actio
import { init as initStatsReporter } from './lib/ui_metric';

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

import { startServices, stopServices, services } from './services';

const { ReadOnlyBadge: strings } = CapabilitiesStrings;

let restoreAction: ActionByType<any> | undefined;
Expand All @@ -51,8 +54,14 @@ export const renderApp = (
{ element }: AppMountParameters,
canvasStore: Store
) => {
const canvasServices = Object.entries(services).reduce((reduction, [key, provider]) => {
reduction[key] = provider.getService();

return reduction;
}, {} as Record<string, any>);

ReactDOM.render(
<KibanaContextProvider services={{ ...plugins, ...coreStart }}>
<KibanaContextProvider services={{ ...plugins, ...coreStart, canvas: canvasServices }}>
<I18nProvider>
<Provider store={canvasStore}>
<App />
Expand All @@ -71,6 +80,8 @@ export const initializeCanvas = async (
startPlugins: CanvasStartDeps,
registries: SetupRegistries
) => {
startServices(coreSetup, coreStart, setupPlugins, startPlugins);

// Create Store
const canvasStore = await createStore(coreSetup, setupPlugins);

Expand Down Expand Up @@ -130,6 +141,7 @@ export const initializeCanvas = async (
};

export const teardownCanvas = (coreStart: CoreStart, startPlugins: CanvasStartDeps) => {
stopServices();
destroyRegistries();
resetInterpreter();

Expand Down
10 changes: 7 additions & 3 deletions x-pack/legacy/plugins/canvas/public/apps/workpad/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { ErrorStrings } from '../../../i18n';
import * as workpadService from '../../lib/workpad_service';
import { notify } from '../../lib/notify';
import { notifyService } from '../../services';
import { getBaseBreadcrumb, getWorkpadBreadcrumb, setBreadcrumb } from '../../lib/breadcrumbs';
import { getDefaultWorkpad } from '../../state/defaults';
import { setWorkpad } from '../../state/actions/workpad';
Expand All @@ -33,7 +33,9 @@ export const routes = [
dispatch(resetAssets());
router.redirectTo('loadWorkpad', { id: newWorkpad.id, page: 1 });
} catch (err) {
notify.error(err, { title: strings.getCreateFailureErrorMessage() });
notifyService
.getService()
.error(err, { title: strings.getCreateFailureErrorMessage() });
router.redirectTo('home');
}
},
Expand All @@ -59,7 +61,9 @@ export const routes = [
// reset transient properties when changing workpads
dispatch(setZoomScale(1));
} catch (err) {
notify.error(err, { title: strings.getLoadFailureErrorMessage() });
notifyService
.getService()
.error(err, { title: strings.getLoadFailureErrorMessage() });
return router.redirectTo('home');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { AssetModal } from './asset_modal';

const { AssetManager: strings } = ComponentStrings;

interface Props {
export interface Props {
/** A list of assets, if available */
assetValues: AssetType[];
/** Function to invoke when an asset is selected to be added as an element to the workpad */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,36 @@ import { connect } from 'react-redux';
import { compose, withProps } from 'recompose';
import { set, get } from 'lodash';
import { fromExpression, toExpression } from '@kbn/interpreter/common';
import { notify } from '../../lib/notify';
import { getAssets } from '../../state/selectors/assets';
// @ts-ignore Untyped local
import { removeAsset, createAsset } from '../../state/actions/assets';
// @ts-ignore Untyped local
import { elementsRegistry } from '../../lib/elements_registry';
// @ts-ignore Untyped local
import { addElement } from '../../state/actions/elements';
import { getSelectedPage } from '../../state/selectors/workpad';
import { encode } from '../../../common/lib/dataurl';
import { getId } from '../../lib/get_id';
// @ts-ignore Untyped Local
import { findExistingAsset } from '../../lib/find_existing_asset';
import { VALID_IMAGE_TYPES } from '../../../common/lib/constants';
import { AssetManager as Component } from './asset_manager';
import { withKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { WithKibanaProps } from '../../';
import { AssetManager as Component, Props as AssetManagerProps } from './asset_manager';

const mapStateToProps = state => ({
import { State, ExpressionAstExpression, AssetType } from '../../../types';

const mapStateToProps = (state: State) => ({
assets: getAssets(state),
selectedPage: getSelectedPage(state),
});

const mapDispatchToProps = dispatch => ({
onAddImageElement: pageId => assetId => {
const mapDispatchToProps = (dispatch: (action: any) => void) => ({
onAddImageElement: (pageId: string) => (assetId: string) => {
const imageElement = elementsRegistry.get('image');
const elementAST = fromExpression(imageElement.expression);
const selector = ['chain', '0', 'arguments', 'dataurl'];
const subExp = [
const subExp: ExpressionAstExpression[] = [
{
type: 'expression',
chain: [
Expand All @@ -44,22 +51,26 @@ const mapDispatchToProps = dispatch => ({
],
},
];
const newAST = set(elementAST, selector, subExp);
const newAST = set<ExpressionAstExpression>(elementAST, selector, subExp);
imageElement.expression = toExpression(newAST);
dispatch(addElement(pageId, imageElement));
},
onAssetAdd: (type, content) => {
onAssetAdd: (type: string, content: string) => {
// make the ID here and pass it into the action
const assetId = getId('asset');
dispatch(createAsset(type, content, assetId));

// then return the id, so the caller knows the id that will be created
return assetId;
},
onAssetDelete: assetId => dispatch(removeAsset(assetId)),
onAssetDelete: (assetId: string) => dispatch(removeAsset(assetId)),
});

const mergeProps = (stateProps, dispatchProps, ownProps) => {
const mergeProps = (
stateProps: ReturnType<typeof mapStateToProps>,
dispatchProps: ReturnType<typeof mapDispatchToProps>,
ownProps: AssetManagerProps
) => {
const { assets, selectedPage } = stateProps;
const { onAssetAdd } = dispatchProps;
const assetValues = Object.values(assets); // pull values out of assets object
Expand All @@ -70,16 +81,16 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
onAddImageElement: dispatchProps.onAddImageElement(stateProps.selectedPage),
selectedPage,
assetValues,
onAssetAdd: file => {
onAssetAdd: (file: File) => {
const [type, subtype] = get(file, 'type', '').split('/');
if (type === 'image' && VALID_IMAGE_TYPES.indexOf(subtype) >= 0) {
return encode(file).then(dataurl => {
const type = 'dataurl';
const existingId = findExistingAsset(type, dataurl, assetValues);
const dataurlType = 'dataurl';
const existingId = findExistingAsset(dataurlType, dataurl, assetValues);
if (existingId) {
return existingId;
}
return onAssetAdd(type, dataurl);
return onAssetAdd(dataurlType, dataurl);
});
}

Expand All @@ -88,7 +99,11 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
};
};

export const AssetManager = compose(
export const AssetManager = compose<any, any>(
connect(mapStateToProps, mapDispatchToProps, mergeProps),
withProps({ onAssetCopy: asset => notify.success(`Copied '${asset.id}' to clipboard`) })
withKibana,
withProps(({ kibana }: WithKibanaProps) => ({
onAssetCopy: (asset: AssetType) =>
kibana.services.canvas.notify.success(`Copied '${asset.id}' to clipboard`),
}))
)(Component);
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { compose, withProps, withPropsOnChange } from 'recompose';
import PropTypes from 'prop-types';
import isEqual from 'react-fast-compare';
import { notify } from '../../lib/notify';
import { withKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { RenderWithFn as Component } from './render_with_fn';
import { ElementHandlers } from './lib/handlers';

Expand All @@ -19,9 +19,10 @@ export const RenderWithFn = compose(
handlers: Object.assign(new ElementHandlers(), handlers),
})
),
withProps({
onError: notify.error,
})
withKibana,
withProps(props => ({
onError: props.kibana.services.canvas.notify.error,
}))
)(Component);

RenderWithFn.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { camelCase } from 'lodash';
// @ts-ignore Untyped local
import { cloneSubgraphs } from '../../lib/clone_subgraphs';
import * as customElementService from '../../lib/custom_element_service';
// @ts-ignore Untyped local
import { notify } from '../../lib/notify';
import { withKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { WithKibanaProps } from '../../';
// @ts-ignore Untyped local
import { selectToplevelNodes } from '../../state/actions/transient';
// @ts-ignore Untyped local
Expand Down Expand Up @@ -64,7 +64,7 @@ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
const mergeProps = (
stateProps: StateProps,
dispatchProps: DispatchProps,
ownProps: OwnPropsWithState
ownProps: OwnPropsWithState & WithKibanaProps
): ComponentProps => {
const { pageId } = stateProps;
const { onClose, search, setCustomElements } = ownProps;
Expand Down Expand Up @@ -92,7 +92,9 @@ const mergeProps = (
try {
await findCustomElements();
} catch (err) {
notify.error(err, { title: `Couldn't find custom elements` });
ownProps.kibana.services.canvas.notify.error(err, {
title: `Couldn't find custom elements`,
});
}
},
// remove custom element
Expand All @@ -101,7 +103,9 @@ const mergeProps = (
await customElementService.remove(id);
await findCustomElements();
} catch (err) {
notify.error(err, { title: `Couldn't delete custom elements` });
ownProps.kibana.services.canvas.notify.error(err, {
title: `Couldn't delete custom elements`,
});
}
},
// update custom element
Expand All @@ -115,13 +119,16 @@ const mergeProps = (
});
await findCustomElements();
} catch (err) {
notify.error(err, { title: `Couldn't update custom elements` });
ownProps.kibana.services.canvas.notify.error(err, {
title: `Couldn't update custom elements`,
});
}
},
};
};

export const SavedElementsModal = compose<ComponentProps, OwnProps>(
withKibana,
withState('search', 'setSearch', ''),
withState('customElements', 'setCustomElements', []),
connect(mapStateToProps, mapDispatchToProps, mergeProps)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
getRenderedWorkpadExpressions,
} from '../../../../state/selectors/workpad';
// @ts-ignore Untyped local
import { notify } from '../../../../lib/notify';
import {
downloadRenderedWorkpad,
downloadRuntime,
Expand Down Expand Up @@ -70,7 +69,7 @@ export const ShareWebsiteFlyout = compose<ComponentProps, Pick<Props, 'onClose'>
unsupportedRenderers,
onClose,
onCopy: () => {
notify.info(strings.getCopyShareConfigMessage());
kibana.services.canvas.notify.info(strings.getCopyShareConfigMessage());
},
onDownload: type => {
switch (type) {
Expand All @@ -86,7 +85,9 @@ export const ShareWebsiteFlyout = compose<ComponentProps, Pick<Props, 'onClose'>
.post(`${basePath}${API_ROUTE_SHAREABLE_ZIP}`, JSON.stringify(renderedWorkpad))
.then(blob => downloadZippedRuntime(blob.data))
.catch((err: Error) => {
notify.error(err, { title: strings.getShareableZipErrorTitle(workpad.name) });
kibana.services.canvas.notify.error(err, {
title: strings.getShareableZipErrorTitle(workpad.name),
});
});
return;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { connect } from 'react-redux';
import { compose, withProps } from 'recompose';
import { jobCompletionNotifications } from '../../../../../../../plugins/reporting/public';
import { getWorkpad, getPages } from '../../../state/selectors/workpad';
// @ts-ignore Untyped local
import { notify } from '../../../lib/notify';
import { getWindow } from '../../../lib/get_window';
import { downloadWorkpad } from '../../../lib/download_workpad';
import { ShareMenu as Component, Props as ComponentProps } from './share_menu';
Expand Down Expand Up @@ -59,10 +57,10 @@ export const ShareMenu = compose<ComponentProps, {}>(
onCopy: type => {
switch (type) {
case 'pdf':
notify.info(strings.getCopyPDFMessage());
kibana.services.canvas.notify.info(strings.getCopyPDFMessage());
break;
case 'reportingConfig':
notify.info(strings.getCopyReportingConfigMessage());
kibana.services.canvas.notify.info(strings.getCopyReportingConfigMessage());
break;
default:
throw new Error(strings.getUnknownExportErrorMessage(type));
Expand All @@ -73,15 +71,17 @@ export const ShareMenu = compose<ComponentProps, {}>(
case 'pdf':
return createPdf(workpad, { pageCount }, kibana.services.http.basePath)
.then(({ data }: { data: { job: { id: string } } }) => {
notify.info(strings.getExportPDFMessage(), {
kibana.services.canvas.notify.info(strings.getExportPDFMessage(), {
title: strings.getExportPDFTitle(workpad.name),
});

// register the job so a completion notification shows up when it's ready
jobCompletionNotifications.add(data.job.id);
})
.catch((err: Error) => {
notify.error(err, { title: strings.getExportPDFErrorTitle(workpad.name) });
kibana.services.canvas.notify.error(err, {
title: strings.getExportPDFErrorTitle(workpad.name),
});
});
case 'json':
downloadWorkpad(workpad.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { compose, withHandlers } from 'recompose';
import { Dispatch } from 'redux';
import { withKibana } from '../../../../../../../../src/plugins/kibana_react/public/';
import { zoomHandlerCreators } from '../../../lib/app_handler_creators';
// @ts-ignore Untyped local
import { notify } from '../../../lib/notify';
import { State, CanvasWorkpadBoundingBox } from '../../../../types';
// @ts-ignore Untyped local
import { fetchAllRenderables } from '../../../state/actions/elements';
Expand Down
Loading

0 comments on commit 3a4b187

Please sign in to comment.