diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts index b9f1191da8af7..e26eeadd4edcd 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts @@ -10,6 +10,12 @@ import { API_BASE_PATH } from '../../../common/constants'; type HttpResponse = Record | any[]; +export interface ResponseError { + statusCode: number; + message: string | Error; + attributes?: Record; +} + // Register helpers to mock HTTP Requests const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { const setLoadTemplatesResponse = (response: HttpResponse = []) => { @@ -101,6 +107,17 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { ]); }; + const setUpdateIndexSettingsResponse = (response?: HttpResponse, error?: ResponseError) => { + const status = error ? error.statusCode || 400 : 200; + const body = error ?? response; + + server.respondWith('PUT', `${API_BASE_PATH}/settings/:name`, [ + status, + { 'Content-Type': 'application/json' }, + JSON.stringify(body), + ]); + }; + const setSimulateTemplateResponse = (response?: HttpResponse, error?: any) => { const status = error ? error.status || 400 : 200; const body = error ? JSON.stringify(error.body) : JSON.stringify(response); @@ -134,6 +151,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { setLoadTemplateResponse, setCreateTemplateResponse, setUpdateTemplateResponse, + setUpdateIndexSettingsResponse, setSimulateTemplateResponse, setLoadComponentTemplatesResponse, }; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts index 96775484e0733..ac4b4c46ad4d1 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts @@ -60,4 +60,6 @@ export type TestSubjects = | 'templateTable' | 'title' | 'unfreezeIndexMenuButton' + | 'updateEditIndexSettingsButton' + | 'updateIndexSettingsErrorCallout' | 'viewButton'; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts index 79fe885820fae..ec80bf5d712c0 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts @@ -12,6 +12,31 @@ import { setupEnvironment, nextTick } from '../helpers'; import { IndicesTestBed, setup } from './indices_tab.helpers'; import { createDataStreamPayload, createNonDataStreamIndex } from './data_streams_tab.helpers'; +// Since the editor component being used for editing index settings is not a React +// component but an editor being instantiated on a div reference, we cannot mock +// the component and replace it with something else. In this particular case we're +// mocking the returned instance of the editor to always have the same values. +const mockGetAceEditorValue = jest.fn().mockReturnValue(`{}`); + +jest.mock('../../../public/application/lib/ace.js', () => { + const createAceEditor = () => { + return { + getValue: mockGetAceEditorValue, + getSession: () => { + return { + on: () => null, + getValue: () => null, + }; + }, + destroy: () => null, + }; + }; + + return { + createAceEditor, + }; +}); + /** * The below import is required to avoid a console error warn from the "brace" package * console.warn ../node_modules/brace/index.js:3999 @@ -212,4 +237,43 @@ describe('', () => { expect(exists('unfreezeIndexMenuButton')).toBe(false); }); }); + + describe('Edit index settings', () => { + const indexName = 'testIndex'; + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadIndicesResponse([createNonDataStreamIndex(indexName)]); + + testBed = await setup(); + const { find, component } = testBed; + component.update(); + + find('indexTableIndexNameLink').at(0).simulate('click'); + }); + + test('shows error callout when request fails', async () => { + const { actions, find, component, exists } = testBed; + + mockGetAceEditorValue.mockReturnValue(`{ + "index.routing.allocation.include._tier_preference": "non_existent_tier" + }`); + + const error = { + statusCode: 400, + error: 'Bad Request', + message: 'invalid tier names found in ...', + }; + httpRequestsMockHelpers.setUpdateIndexSettingsResponse(undefined, error); + + await actions.selectIndexDetailsTab('edit_settings'); + + await act(async () => { + find('updateEditIndexSettingsButton').simulate('click'); + }); + + component.update(); + + expect(exists('updateIndexSettingsErrorCallout')).toBe(true); + }); + }); }); diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js index 2337485e6c82b..55581190b89b8 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js @@ -6,6 +6,7 @@ */ import React from 'react'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { documentationService } from '../../../../../services/documentation'; @@ -13,10 +14,9 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem, - EuiIcon, + EuiCallOut, EuiLink, EuiSpacer, - EuiTextColor, EuiTitle, } from '@elastic/eui'; import { TAB_SETTINGS } from '../../../../../constants'; @@ -90,16 +90,25 @@ export class EditSettingsJson extends React.PureComponent { }; errorMessage() { const { error } = this.props; + if (!error) { return null; } + return ( -
- - - {error} + <> -
+ +

{error}

+
+ ); } render() { @@ -135,6 +144,7 @@ export class EditSettingsJson extends React.PureComponent { + {this.errorMessage()} - {this.errorMessage()} ); diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts index 5cfb881cb22cf..972d7c6c87f36 100644 --- a/x-pack/plugins/index_management/public/application/services/api.ts +++ b/x-pack/plugins/index_management/public/application/services/api.ts @@ -194,14 +194,17 @@ export async function loadIndexSettings(indexName: string) { } export async function updateIndexSettings(indexName: string, body: object) { - const response = await httpService.httpClient.put( - `${API_BASE_PATH}/settings/${encodeURIComponent(indexName)}`, - { - body: JSON.stringify(body), - } - ); + const response = await sendRequest({ + path: `${API_BASE_PATH}/settings/${encodeURIComponent(indexName)}`, + method: 'put', + body: JSON.stringify(body), + }); + // Only track successful requests. - uiMetricService.trackMetric(METRIC_TYPE.COUNT, UIM_UPDATE_SETTINGS); + if (!response.error) { + uiMetricService.trackMetric(METRIC_TYPE.COUNT, UIM_UPDATE_SETTINGS); + } + return response; } diff --git a/x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js b/x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js index bbb251634976e..22ebf78fc5fe0 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js +++ b/x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js @@ -22,13 +22,9 @@ export const updateIndexSettings = ({ indexName, settings }) => async (dispatch) => { if (Object.keys(settings).length !== 0) { - try { - const { error, message } = await request(indexName, settings); + const { error } = await request(indexName, settings); - if (error) { - return dispatch(updateIndexSettingsError({ error: message })); - } - } catch (error) { + if (error) { return dispatch(updateIndexSettingsError({ error: error.message })); } }