[Lens] Leverage original http request error (#79831)

This commit is contained in:
Joe Reuter 2020-10-13 15:17:40 +02:00 committed by GitHub
parent 16e1598c05
commit cd84ace53d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 105 additions and 10 deletions

View file

@ -14,5 +14,6 @@ export interface ExpressionRenderError extends Error
| Property | Type | Description |
| --- | --- | --- |
| [original](./kibana-plugin-plugins-expressions-public.expressionrendererror.original.md) | <code>Error</code> | |
| [type](./kibana-plugin-plugins-expressions-public.expressionrendererror.type.md) | <code>string</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [ExpressionRenderError](./kibana-plugin-plugins-expressions-public.expressionrendererror.md) &gt; [original](./kibana-plugin-plugins-expressions-public.expressionrendererror.original.md)
## ExpressionRenderError.original property
<b>Signature:</b>
```typescript
original?: Error;
```

View file

@ -20,5 +20,5 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams
| [onEvent](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md) | <code>(event: ExpressionRendererEvent) =&gt; void</code> | |
| [padding](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md) | <code>'xs' &#124; 's' &#124; 'm' &#124; 'l' &#124; 'xl'</code> | |
| [reload$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md) | <code>Observable&lt;unknown&gt;</code> | An observable which can be used to re-run the expression without destroying the component |
| [renderError](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.rendererror.md) | <code>(error?: string &#124; null) =&gt; React.ReactElement &#124; React.ReactElement[]</code> | |
| [renderError](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.rendererror.md) | <code>(message?: string &#124; null, error?: ExpressionRenderError &#124; null) =&gt; React.ReactElement &#124; React.ReactElement[]</code> | |

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
renderError?: (error?: string | null) => React.ReactElement | React.ReactElement[];
renderError?: (message?: string | null, error?: ExpressionRenderError | null) => React.ReactElement | React.ReactElement[];
```

View file

@ -40,6 +40,11 @@ export const createError = (err: string | ErrorLike): ExpressionValueError => ({
: undefined,
message: typeof err === 'string' ? err : String(err.message),
name: typeof err === 'object' ? err.name || 'Error' : 'Error',
original: err instanceof Error ? (err as SerializedError) : undefined,
original:
err instanceof Error
? err
: typeof err === 'object' && 'original' in err && err.original instanceof Error
? err.original
: undefined,
},
});

View file

@ -511,6 +511,8 @@ export class ExpressionRendererRegistry implements IRegistry<ExpressionRenderer>
//
// @public (undocumented)
export interface ExpressionRenderError extends Error {
// (undocumented)
original?: Error;
// (undocumented)
type?: string;
}
@ -1095,7 +1097,7 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams {
padding?: 'xs' | 's' | 'm' | 'l' | 'xl';
reload$?: Observable<unknown>;
// (undocumented)
renderError?: (error?: string | null) => React.ReactElement | React.ReactElement[];
renderError?: (message?: string | null, error?: ExpressionRenderError | null) => React.ReactElement | React.ReactElement[];
}
// Warning: (ae-missing-release-tag) "ReactExpressionRendererType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)

View file

@ -35,7 +35,10 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams {
className?: string;
dataAttrs?: string[];
expression: string | ExpressionAstExpression;
renderError?: (error?: string | null) => React.ReactElement | React.ReactElement[];
renderError?: (
message?: string | null,
error?: ExpressionRenderError | null
) => React.ReactElement | React.ReactElement[];
padding?: 'xs' | 's' | 'm' | 'l' | 'xl';
onEvent?: (event: ExpressionRendererEvent) => void;
/**
@ -186,7 +189,10 @@ export const ReactExpressionRenderer = ({
<div {...dataAttrs} className={classes}>
{state.isEmpty && <EuiLoadingChart mono size="l" />}
{state.isLoading && <EuiProgress size="xs" color="accent" position="absolute" />}
{!state.isLoading && state.error && renderError && renderError(state.error.message)}
{!state.isLoading &&
state.error &&
renderError &&
renderError(state.error.message, state.error)}
<div
className="expExpressionRenderer__expression"
style={expressionStyles}

View file

@ -55,6 +55,7 @@ export interface IExpressionLoaderParams {
export interface ExpressionRenderError extends Error {
type?: string;
original?: Error;
}
export type RenderErrorHandlerFnType = (

View file

@ -14,6 +14,7 @@ import { CoreStart, CoreSetup } from 'kibana/public';
import { ExecutionContextSearch } from 'src/plugins/expressions';
import {
ExpressionRendererEvent,
ExpressionRenderError,
ReactExpressionRendererType,
} from '../../../../../../../src/plugins/expressions/public';
import { Action } from '../state_management';
@ -40,6 +41,7 @@ import {
} from '../../../../../../../src/plugins/data/public';
import { WorkspacePanelWrapper } from './workspace_panel_wrapper';
import { DropIllustration } from '../../../assets/drop_illustration';
import { getOriginalRequestErrorMessage } from '../../error_helper';
export interface WorkspacePanelProps {
activeVisualizationId: string | null;
@ -342,7 +344,8 @@ export const InnerVisualizationWrapper = ({
searchContext={context}
reload$={autoRefreshFetch$}
onEvent={onEvent}
renderError={(errorMessage?: string | null) => {
renderError={(errorMessage?: string | null, error?: ExpressionRenderError | null) => {
const visibleErrorMessage = getOriginalRequestErrorMessage(error) || errorMessage;
return (
<EuiFlexGroup style={{ maxWidth: '100%' }} direction="column" alignItems="center">
<EuiFlexItem>
@ -354,7 +357,7 @@ export const InnerVisualizationWrapper = ({
defaultMessage="An error occurred when loading data."
/>
</EuiFlexItem>
{errorMessage ? (
{visibleErrorMessage ? (
<EuiFlexItem className="eui-textBreakAll" grow={false}>
<EuiButtonEmpty
onClick={() => {
@ -369,7 +372,7 @@ export const InnerVisualizationWrapper = ({
})}
</EuiButtonEmpty>
{localState.expandError ? errorMessage : null}
{localState.expandError ? visibleErrorMessage : null}
</EuiFlexItem>
) : null}
</EuiFlexGroup>

View file

@ -13,6 +13,7 @@ import {
ReactExpressionRendererType,
} from 'src/plugins/expressions/public';
import { ExecutionContextSearch } from 'src/plugins/expressions';
import { getOriginalRequestErrorMessage } from '../error_helper';
export interface ExpressionWrapperProps {
ExpressionRenderer: ReactExpressionRendererType;
@ -50,7 +51,20 @@ export function ExpressionWrapper({
padding="m"
expression={expression}
searchContext={searchContext}
renderError={(error) => <div data-test-subj="expression-renderer-error">{error}</div>}
renderError={(errorMessage, error) => (
<div data-test-subj="expression-renderer-error">
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
<EuiFlexItem>
<EuiIcon type="alert" color="danger" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="s">
{getOriginalRequestErrorMessage(error) || errorMessage}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</div>
)}
onEvent={handleEvent}
/>
</div>

View file

@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { ExpressionRenderError } from 'src/plugins/expressions/public';
interface ElasticsearchErrorClause {
type: string;
reason: string;
caused_by?: ElasticsearchErrorClause;
}
interface RequestError extends Error {
body?: { attributes?: { error: ElasticsearchErrorClause } };
}
const isRequestError = (e: Error | RequestError): e is RequestError => {
if ('body' in e) {
return e.body?.attributes?.error?.caused_by !== undefined;
}
return false;
};
function getNestedErrorClause({
type,
reason,
caused_by: causedBy,
}: ElasticsearchErrorClause): { type: string; reason: string } {
if (causedBy) {
return getNestedErrorClause(causedBy);
}
return { type, reason };
}
export function getOriginalRequestErrorMessage(error?: ExpressionRenderError | null) {
if (error && 'original' in error && error.original && isRequestError(error.original)) {
const rootError = getNestedErrorClause(error.original.body!.attributes!.error);
if (rootError.reason && rootError.type) {
return i18n.translate('xpack.lens.editorFrame.expressionFailureMessage', {
defaultMessage: 'Request error: {type}, {reason}',
values: {
reason: rootError.reason,
type: rootError.type,
},
});
}
}
}