Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lens] show 'View details' UI action to open clusters inspector tab when request fails #172971

Merged
merged 25 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1f1aa96
move lens EsError functionallity into search-errors package
nreese Dec 8, 2023
0505817
update lens
nreese Dec 8, 2023
1bd6cd5
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 8, 2023
e7930e0
Merge branch 'main' into issue_171570
kibanamachine Dec 8, 2023
a2c7f4a
PaginatedErrors
nreese Dec 8, 2023
6a51c26
i18n clean up
nreese Dec 8, 2023
9ad7180
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 8, 2023
6b2b728
rename
nreese Dec 8, 2023
24313c1
use renderSearchError in EsError tests
nreese Dec 8, 2023
5534753
rename
nreese Dec 8, 2023
43694f4
fix workspace panel test
nreese Dec 8, 2023
db24014
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 8, 2023
7465c78
fix functional tests
nreese Dec 10, 2023
fa413a6
fix lens tests
nreese Dec 10, 2023
5b5504f
Merge branch 'main' into issue_171570
kibanamachine Dec 10, 2023
4417b73
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Dec 10, 2023
0d443fb
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 10, 2023
1a79bc4
clean up
nreese Dec 10, 2023
2090896
merge with main
nreese Dec 16, 2023
915a960
fix merge conflict
nreese Dec 16, 2023
68632e8
fix import
nreese Dec 16, 2023
1241357
Merge branch 'main' into issue_171570
kibanamachine Dec 17, 2023
83fe5ac
Update packages/kbn-search-errors/src/painless_error.tsx
nreese Dec 20, 2023
b6ab775
return empty array instead of array with empty string
nreese Dec 20, 2023
ea17613
Merge branch 'main' into issue_171570
kibanamachine Dec 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/kbn-search-errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

export { createEsError } from './src/create_es_error';
export { isEsError, EsError } from './src/es_error';
export { isPainlessError, PainlessError } from './src/painless_error';
export { renderSearchError } from './src/render_search_error';
export type { IEsError } from './src/types';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions packages/kbn-search-errors/src/create_es_error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { estypes } from '@elastic/elasticsearch';
import { i18n } from '@kbn/i18n';
import type { ApplicationStart, CoreStart } from '@kbn/core/public';
import type { DataView } from '@kbn/data-views-plugin/common';
import { IEsError } from './types';
import { EsError } from './es_error';
import { PainlessError } from './painless_error';
import { TsdbError } from './tsdb_error';

export interface Services {
application: ApplicationStart;
docLinks: CoreStart['docLinks'];
}

function getNestedCauses(errorCause: estypes.ErrorCause): estypes.ErrorCause[] {
// Give shard failures priority, then try to get the error navigating nested objects
if (errorCause.failed_shards) {
return (errorCause.failed_shards as estypes.ShardFailure[]).map(
(shardFailure) => shardFailure.reason
);
}
return errorCause.caused_by ? getNestedCauses(errorCause.caused_by) : [errorCause];
}

export function createEsError(
err: IEsError,
openInInspector: () => void,
services: Services,
dataView?: DataView
) {
const rootCauses = err.attributes?.error ? getNestedCauses(err.attributes?.error) : [];

const painlessCause = rootCauses.find((errorCause) => {
return errorCause.lang && errorCause.lang === 'painless';
});
if (painlessCause) {
return new PainlessError(err, openInInspector, painlessCause, services.application, dataView);
}

const tsdbCause = rootCauses.find((errorCause) => {
return (
errorCause.type === 'illegal_argument_exception' &&
errorCause.reason &&
/\]\[counter\] is not supported for aggregation/.test(errorCause.reason)
);
});
if (tsdbCause) {
return new TsdbError(err, openInInspector, tsdbCause, services.docLinks);
}

const causeReason = rootCauses[0]?.reason ?? err.attributes?.error?.reason;
const message = causeReason
? causeReason
: i18n.translate('searchErrors.esError.unknownRootCause', { defaultMessage: 'unknown' });
return new EsError(err, message, openInInspector);
}
71 changes: 39 additions & 32 deletions packages/kbn-search-errors/src/es_error.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,31 @@
* Side Public License, v 1.
*/

import { EsError } from './es_error';
import { IEsError } from './types';
import type { ReactElement } from 'react';
import type { CoreStart } from '@kbn/core/public';
import { createEsError } from './create_es_error';
import { renderSearchError } from './render_search_error';
import { shallow } from 'enzyme';
import { coreMock } from '@kbn/core/public/mocks';

describe('EsError', () => {
it('contains the same body as the wrapped error', () => {
const error = {
statusCode: 500,
message: 'nope',
attributes: {
error: {
type: 'top_level_exception_type',
reason: 'top-level reason',
},
const services = {
application: coreMock.createStart().application,
docLinks: {
links: {
fleet: {
datastreamsTSDSMetrics: '',
},
} as IEsError;
const esError = new EsError(error, () => {});

expect(typeof esError.attributes).toEqual('object');
expect(esError.attributes).toEqual(error.attributes);
});
},
} as CoreStart['docLinks'],
};

it('contains some explanation of the error in the message', () => {
// error taken from Vega's issue
const error = {
message:
'x_content_parse_exception: [x_content_parse_exception] Reason: [1:78] [date_histogram] failed to parse field [calendar_interval]',
describe('EsError', () => {
const esError = createEsError(
{
statusCode: 400,
message: 'search_phase_execution_exception',
attributes: {
error: {
root_cause: [
{
type: 'x_content_parse_exception',
reason: '[1:78] [date_histogram] failed to parse field [calendar_interval]',
},
],
type: 'x_content_parse_exception',
reason: '[1:78] [date_histogram] failed to parse field [calendar_interval]',
caused_by: {
Expand All @@ -49,10 +39,27 @@ describe('EsError', () => {
},
},
},
} as IEsError;
const esError = new EsError(error, () => {});
},
() => {},
services
);

test('should set error.message to root "error cause" reason', () => {
expect(esError.message).toEqual(
'EsError: The supplied interval [2q] could not be parsed as a calendar interval.'
'The supplied interval [2q] could not be parsed as a calendar interval.'
);
});

test('should render error message', () => {
const searchErrorDisplay = renderSearchError(esError);
expect(searchErrorDisplay).not.toBeUndefined();
const wrapper = shallow(searchErrorDisplay?.body as ReactElement);
expect(wrapper).toMatchSnapshot();
});

test('should return 1 action', () => {
const searchErrorDisplay = renderSearchError(esError);
expect(searchErrorDisplay).not.toBeUndefined();
expect(searchErrorDisplay?.actions?.length).toBe(1);
});
});
38 changes: 11 additions & 27 deletions packages/kbn-search-errors/src/es_error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
*/

import React from 'react';
import { EuiButton, EuiCodeBlock, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { ApplicationStart } from '@kbn/core/public';
import { IEsError } from './types';
import { getRootCause } from './utils';
import { EuiButton, EuiCodeBlock } from '@elastic/eui';
import type { IEsError } from './types';

/**
* Checks if a given errors originated from Elasticsearch.
Expand All @@ -24,39 +22,25 @@ export function isEsError(e: any): e is IEsError {
}

export class EsError extends Error {
readonly attributes: IEsError['attributes'];
public readonly attributes: IEsError['attributes'];
private readonly openInInspector: () => void;

constructor(protected readonly err: IEsError, private readonly openInInspector: () => void) {
super(
`EsError: ${
getRootCause(err?.attributes?.error)?.reason ||
i18n.translate('searchErrors.esError.unknownRootCause', { defaultMessage: 'unknown' })
}`
);
constructor(err: IEsError, message: string, openInInspector: () => void) {
super(message);
this.attributes = err.attributes;
this.openInInspector = openInInspector;
Object.setPrototypeOf(this, new.target.prototype);
}

public getErrorMessage() {
if (!this.attributes?.error) {
return null;
}

const rootCause = getRootCause(this.attributes.error)?.reason;
const topLevelCause = this.attributes.error.reason;
const cause = rootCause ?? topLevelCause;

return (
<>
<EuiSpacer size="s" />
<EuiCodeBlock data-test-subj="errMessage" isCopyable={true} paddingSize="s">
{cause}
</EuiCodeBlock>
</>
<EuiCodeBlock data-test-subj="errMessage" isCopyable={true} paddingSize="s">
{this.message}
</EuiCodeBlock>
);
}

public getActions(application: ApplicationStart) {
public getActions() {
return [
<EuiButton
data-test-subj="viewEsErrorButton"
Expand Down
Loading