diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.test.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.test.ts new file mode 100644 index 0000000000000..cff179f64ea08 --- /dev/null +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.test.ts @@ -0,0 +1,71 @@ +/* + * 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 { errors } from '@elastic/elasticsearch'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { kibanaResponseFactory as response } from 'src/core/server'; +import { handleEsError } from './handle_es_error'; + +const { ResponseError } = errors; + +const anyObject: any = {}; + +describe('handleEsError', () => { + test('top-level reason is an empty string', () => { + const emptyReasonError = new ResponseError({ + warnings: [], + meta: anyObject, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: '', // Empty reason + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'too_many_buckets_exception', + reason: 'This is the nested reason', + max_buckets: 100, + }, + }, + }, + statusCode: 503, + headers: {}, + }); + + const { payload, status } = handleEsError({ error: emptyReasonError, response }); + + expect(payload.message).toEqual('This is the nested reason'); + expect(status).toBe(503); + }); + + test('empty error', () => { + const { payload, status } = handleEsError({ + error: new ResponseError({ + body: {}, + statusCode: 400, + headers: {}, + meta: anyObject, + warnings: [], + }), + response, + }); + + expect(payload).toEqual({ + attributes: { causes: undefined, error: undefined }, + message: 'Response Error', + }); + + expect(status).toBe(400); + }); + + test('unknown object', () => { + expect(() => handleEsError({ error: anyObject, response })).toThrow(); + }); +}); diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts index 6a308203fcc27..678c46f69d51f 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts @@ -38,12 +38,14 @@ export const handleEsError = ({ return response.customError({ statusCode, body: { - message: body.error?.reason ?? error.message ?? 'Unknown error', + message: + // We use || instead of ?? as the switch here because reason could be an empty string + body?.error?.reason || body?.error?.caused_by?.reason || error.message || 'Unknown error', attributes: { // The full original ES error object - error: body.error, + error: body?.error, // We assume that this is an ES error object with a nested caused by chain if we can see the "caused_by" field at the top-level - causes: body.error?.caused_by ? getEsCause(body.error) : undefined, + causes: body?.error?.caused_by ? getEsCause(body.error) : undefined, }, }, });