Skip to content

Commit

Permalink
[Canvas] Fixes Uploaded asset not being saved. (#133166)
Browse files Browse the repository at this point in the history
* Fixed upload assets and update workpad logic.

* Fixed onAssetAdd type.

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
Kuznietsov and kibanamachine authored Jun 9, 2022
1 parent 3df948c commit 6beedb6
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 174 deletions.
2 changes: 1 addition & 1 deletion src/plugins/presentation_util/common/lib/utils/dataurl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function encode(data: any | null, type = 'text/plain') {
if (FileReader) {
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result as string);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (err) => reject(err);
reader.readAsDataURL(data);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ import { reduxDecorator, getAddonPanelParameters } from '../../../../storybook';
import { AssetManager, AssetManagerComponent } from '..';
import { assets } from './assets';

const promiseAction =
(actionName: string) =>
(...args: any[]): Promise<string | void> => {
action(actionName)(...args);
return Promise.resolve();
};

storiesOf('components/Assets/AssetManager', module)
.addDecorator(reduxDecorator({ assets }))
.addParameters(getAddonPanelParameters())
Expand All @@ -21,13 +28,13 @@ storiesOf('components/Assets/AssetManager', module)
<AssetManagerComponent
assets={[]}
onClose={action('onClose')}
onAddAsset={action('onAddAsset')}
onAddAsset={promiseAction('onAddAsset')}
/>
))
.add('two assets', () => (
<AssetManagerComponent
assets={assets}
onClose={action('onClose')}
onAddAsset={action('onAddAsset')}
onAddAsset={promiseAction('onAddAsset')}
/>
));
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export interface Props {
assets: AssetType[];
/** Function to invoke when the modal is closed */
onClose: () => void;
onAddAsset: (file: File) => void;
onAddAsset: (file: File) => Promise<void | string>;
}

export const AssetManager: FC<Props> = (props) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,39 @@ import { encode } from '@kbn/presentation-util-plugin/public';
// @ts-expect-error untyped local
import { findExistingAsset } from '../../lib/find_existing_asset';
import { VALID_IMAGE_TYPES } from '../../../common/lib/constants';
import { getId } from '../../lib/get_id';
import { createAsset, notifyError } from '../../lib/assets';
import { getAssets } from '../../state/selectors/assets';
// @ts-expect-error untyped local
import { createAsset } from '../../state/actions/assets';
import { State, AssetType } from '../../../types';
import { setAsset } from '../../state/actions/assets';
import { State, AssetType, CanvasWorkpad } from '../../../types';

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

export const AssetManager = connect(
(state: State) => ({
assets: getAssets(state),
workpad: getFullWorkpadPersisted(state),
}),
(dispatch: Dispatch) => ({
onAddAsset: (type: string, content: string) => {
onAddAsset: (workpad: CanvasWorkpad, type: AssetType['type'], content: AssetType['value']) => {
// make the ID here and pass it into the action
const assetId = getId('asset');
dispatch(createAsset(type, content, assetId));
const asset = createAsset(type, content);
const { notify, workpad: workpadService } = pluginServices.getServices();

// then return the id, so the caller knows the id that will be created
return assetId;
return workpadService
.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, notify.error));
},
}),
(stateProps, dispatchProps, ownProps) => {
const { assets } = stateProps;
const { assets, workpad } = stateProps;
const { onAddAsset } = dispatchProps;

// pull values out of assets object
Expand All @@ -45,22 +54,20 @@ export const AssetManager = connect(
return {
...ownProps,
assets: assetValues,
onAddAsset: (file: File) => {
onAddAsset: async (file: File) => {
const [type, subtype] = get(file, 'type', '').split('/');
if (type === 'image' && VALID_IMAGE_TYPES.indexOf(subtype) >= 0) {
return encode(file).then((dataurl) => {
return await encode(file).then((dataurl) => {
const dataurlType = 'dataurl';
const existingId = findExistingAsset(dataurlType, dataurl, assetValues);

if (existingId) {
return existingId;
}

return onAddAsset(dataurlType, dataurl);
return onAddAsset(workpad, dataurlType, dataurl);
});
}

return false;
},
};
}
Expand Down
27 changes: 20 additions & 7 deletions x-pack/plugins/canvas/public/components/function_form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { Ast } from '@kbn/interpreter';
import deepEqual from 'react-fast-compare';
import { ExpressionAstExpression, ExpressionValue } from '@kbn/expressions-plugin';
import { findExpressionType } from '../../lib/find_expression_type';
import { getId } from '../../lib/get_id';

// @ts-expect-error unconverted action function
import { createAsset } from '../../state/actions/assets';
import { setAsset } from '../../state/actions/assets';
import {
fetchContext,
setArgument as setArgumentValue,
Expand All @@ -26,13 +26,16 @@ import {
getSelectedPage,
getContextForIndex,
getGlobalFilterGroups,
getFullWorkpadPersisted,
} from '../../state/selectors/workpad';
import { getAssets } from '../../state/selectors/assets';
// @ts-expect-error unconverted lib
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';

interface FunctionFormProps {
name: string;
Expand All @@ -51,6 +54,9 @@ 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>(
(state) => getContextForIndex(state, parentPath, expressionIndex),
Expand All @@ -67,6 +73,8 @@ export const FunctionForm: React.FunctionComponent<FunctionFormProps> = (props)
shallowEqual
);

const workpad = useSelector((state: State) => getFullWorkpadPersisted(state));

const addArgument = useCallback(
(argName: string, argValue: string | Ast | null) => () => {
dispatch(addArgumentValue({ element, pageId, argName, value: argValue, path }));
Expand Down Expand Up @@ -99,13 +107,18 @@ export const FunctionForm: React.FunctionComponent<FunctionFormProps> = (props)
const onAssetAddDispatch = useCallback(
(type: AssetType['type'], content: AssetType['value']) => {
// make the ID here and pass it into the action
const assetId = getId('asset');
dispatch(createAsset(type, content, assetId));
const asset = createAsset(type, content);

// then return the id, so the caller knows the id that will be created
return assetId;
return service
.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));
},
[dispatch]
[dispatch, notifyService, service, workpad.assets, workpad.id]
);

const onAssetAdd = useCallback(
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/canvas/public/expression_types/arg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export interface DataArg {
nextArgType?: ArgType;
nextExpressionType?: ExpressionType;
onValueAdd: (argName: string, argValue: ArgValue | null) => () => void;
onAssetAdd: (type: AssetType['type'], content: AssetType['value']) => string;
onAssetAdd: (type: AssetType['type'], content: AssetType['value']) => Promise<void | string>;
onValueChange: (value: Ast | string) => void;
onValueRemove: () => void;
updateContext: (element?: CanvasElement) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export type RenderArgData = BaseFormProps & {
onValueChange: (argName: string, argIndex: number) => (value: string | Ast) => void;
onValueRemove: (argName: string, argIndex: number) => () => void;
onContainerRemove: () => void;
onAssetAdd: (type: AssetType['type'], content: AssetType['value']) => string;
onAssetAdd: (type: AssetType['type'], content: AssetType['value']) => Promise<string | void>;
updateContext: (element?: CanvasElement) => void;
typeInstance?: ExpressionType;
};
Expand Down
51 changes: 51 additions & 0 deletions x-pack/plugins/canvas/public/lib/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { AssetType, CanvasAsset } from '../../types';
import { CanvasNotifyService } from '../services/notify';
import { getId } from './get_id';

const strings = {
getSaveFailureTitle: () =>
i18n.translate('xpack.canvas.error.esPersist.saveFailureTitle', {
defaultMessage: "Couldn't save your changes to Elasticsearch",
}),
getTooLargeErrorMessage: () =>
i18n.translate('xpack.canvas.error.esPersist.tooLargeErrorMessage', {
defaultMessage:
'The server gave a response that the workpad data was too large. This usually means uploaded image assets that are too large for Kibana or a proxy. Try removing some assets in the asset manager.',
}),
getUpdateFailureTitle: () =>
i18n.translate('xpack.canvas.error.esPersist.updateFailureTitle', {
defaultMessage: "Couldn't update workpad",
}),
};

export const createAsset = (type: AssetType['type'], content: AssetType['value']): CanvasAsset => ({
id: getId('asset'),
type,
value: content,
'@created': new Date().toISOString(),
});

export const notifyError = (err: any, notifyErrorFn: CanvasNotifyService['error']) => {
const statusCode = err.response && err.response.status;
switch (statusCode) {
case 400:
return notifyErrorFn(err.response, {
title: strings.getSaveFailureTitle(),
});
case 413:
return notifyErrorFn(strings.getTooLargeErrorMessage(), {
title: strings.getSaveFailureTitle(),
});
default:
return notifyErrorFn(err, {
title: strings.getUpdateFailureTitle(),
});
}
};
Loading

0 comments on commit 6beedb6

Please sign in to comment.