diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/home.helpers.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/home.helpers.ts index 238d3e04404932..71930efe129530 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/home.helpers.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/home.helpers.ts @@ -22,7 +22,7 @@ const testBedConfig: TestBedConfig = { const initTestBed = registerTestBed(WithAppDependencies(SnapshotRestoreHome), testBedConfig); -export interface HomeTestBed extends TestBed { +export interface HomeTestBed extends TestBed { actions: { clickReloadButton: () => void; selectRepositoryAt: (index: number) => void; @@ -115,271 +115,3 @@ export const setup = async (): Promise => { }, }; }; - -type HomeTestSubjects = TestSubjects | ThreeLevelDepth | NonVisibleTestSubjects; - -type NonVisibleTestSubjects = - | 'snapshotDetail.sectionLoading' - | 'sectionLoading' - | 'emptyPrompt' - | 'emptyPrompt.documentationLink' - | 'emptyPrompt.title' - | 'emptyPrompt.registerRepositoryButton' - | 'repositoryDetail.sectionLoading' - | 'snapshotDetail.indexFailure'; - -type ThreeLevelDepth = - | 'snapshotDetail.uuid.value' - | 'snapshotDetail.state.value' - | 'snapshotDetail.version.value' - | 'snapshotDetail.includeGlobalState.value' - | 'snapshotDetail.indices.title' - | 'snapshotDetail.startTime.value' - | 'snapshotDetail.endTime.value' - | 'snapshotDetail.indexFailure.index' - | 'snapshotDetail.indices.value'; - -export type TestSubjects = - | 'appTitle' - | 'cell' - | 'cell.repositoryLink' - | 'cell.snapshotLink' - | 'checkboxSelectAll' - | 'checkboxSelectRow-my-repo' - | 'closeButton' - | 'content' - | 'content.documentationLink' - | 'content.duration' - | 'content.endTime' - | 'content.includeGlobalState' - | 'content.indices' - | 'content.repositoryType' - | 'content.snapshotCount' - | 'content.startTime' - | 'content.state' - | 'content.title' - | 'content.uuid' - | 'content.value' - | 'content.verifyRepositoryButton' - | 'content.version' - | 'deleteRepositoryButton' - | 'detailTitle' - | 'documentationLink' - | 'duration' - | 'duration.title' - | 'duration.value' - | 'editRepositoryButton' - | 'endTime' - | 'endTime.title' - | 'endTime.value' - | 'euiFlyoutCloseButton' - | 'includeGlobalState' - | 'includeGlobalState.title' - | 'includeGlobalState.value' - | 'indices' - | 'indices.title' - | 'indices.value' - | 'registerRepositoryButton' - | 'reloadButton' - | 'repositoryDetail' - | 'repositoryDetail.content' - | 'repositoryDetail.documentationLink' - | 'repositoryDetail.euiFlyoutCloseButton' - | 'repositoryDetail.repositoryType' - | 'repositoryDetail.snapshotCount' - | 'repositoryDetail.srRepositoryDetailsDeleteActionButton' - | 'repositoryDetail.srRepositoryDetailsFlyoutCloseButton' - | 'repositoryDetail.title' - | 'repositoryDetail.verifyRepositoryButton' - | 'repositoryLink' - | 'repositoryList' - | 'repositoryList.cell' - | 'repositoryList.checkboxSelectAll' - | 'repositoryList.checkboxSelectRow-my-repo' - | 'repositoryList.content' - | 'repositoryList.deleteRepositoryButton' - | 'repositoryList.documentationLink' - | 'repositoryList.editRepositoryButton' - | 'repositoryList.euiFlyoutCloseButton' - | 'repositoryList.registerRepositoryButton' - | 'repositoryList.reloadButton' - | 'repositoryList.repositoryDetail' - | 'repositoryList.repositoryLink' - | 'repositoryList.repositoryTable' - | 'repositoryList.repositoryType' - | 'repositoryList.row' - | 'repositoryList.snapshotCount' - | 'repositoryList.srRepositoryDetailsDeleteActionButton' - | 'repositoryList.srRepositoryDetailsFlyoutCloseButton' - | 'repositoryList.tableHeaderCell_name_0' - | 'repositoryList.tableHeaderCell_type_1' - | 'repositoryList.tableHeaderSortButton' - | 'repositoryList.title' - | 'repositoryList.verifyRepositoryButton' - | 'repositoryTable' - | 'repositoryTable.cell' - | 'repositoryTable.checkboxSelectAll' - | 'repositoryTable.checkboxSelectRow-my-repo' - | 'repositoryTable.deleteRepositoryButton' - | 'repositoryTable.editRepositoryButton' - | 'repositoryTable.repositoryLink' - | 'repositoryTable.row' - | 'repositoryTable.tableHeaderCell_name_0' - | 'repositoryTable.tableHeaderCell_type_1' - | 'repositoryTable.tableHeaderSortButton' - | 'repositoryType' - | 'row' - | 'row.cell' - | 'row.checkboxSelectRow-my-repo' - | 'row.deleteRepositoryButton' - | 'row.editRepositoryButton' - | 'row.repositoryLink' - | 'row.snapshotLink' - | 'snapshotCount' - | 'snapshotDetail' - | 'snapshotDetail.closeButton' - | 'snapshotDetail.content' - | 'snapshotDetail.detailTitle' - | 'snapshotDetail.duration' - | 'snapshotDetail.endTime' - | 'snapshotDetail.euiFlyoutCloseButton' - | 'snapshotDetail.includeGlobalState' - | 'snapshotDetail.indices' - | 'snapshotDetail.repositoryLink' - | 'snapshotDetail.startTime' - | 'snapshotDetail.state' - | 'snapshotDetail.tab' - | 'snapshotDetail.title' - | 'snapshotDetail.uuid' - | 'snapshotDetail.value' - | 'snapshotDetail.version' - | 'snapshotLink' - | 'snapshotList' - | 'snapshotListEmpty' - | 'snapshotList.cell' - | 'snapshotList.closeButton' - | 'snapshotList.content' - | 'snapshotList.detailTitle' - | 'snapshotList.duration' - | 'snapshotList.endTime' - | 'snapshotList.euiFlyoutCloseButton' - | 'snapshotList.includeGlobalState' - | 'snapshotList.indices' - | 'snapshotList.reloadButton' - | 'snapshotList.repositoryLink' - | 'snapshotList.row' - | 'snapshotList.snapshotDetail' - | 'snapshotList.snapshotLink' - | 'snapshotList.snapshotTable' - | 'snapshotList.startTime' - | 'snapshotList.state' - | 'snapshotList.tab' - | 'snapshotList.tableHeaderCell_durationInMillis_3' - | 'snapshotList.tableHeaderCell_indices_4' - | 'snapshotList.tableHeaderCell_repository_1' - | 'snapshotList.tableHeaderCell_snapshot_0' - | 'snapshotList.tableHeaderCell_startTimeInMillis_2' - | 'snapshotList.tableHeaderSortButton' - | 'snapshotList.title' - | 'snapshotList.uuid' - | 'snapshotList.value' - | 'snapshotList.version' - | 'snapshotRestoreApp' - | 'snapshotRestoreApp.appTitle' - | 'snapshotRestoreApp.cell' - | 'snapshotRestoreApp.checkboxSelectAll' - | 'snapshotRestoreApp.checkboxSelectRow-my-repo' - | 'snapshotRestoreApp.closeButton' - | 'snapshotRestoreApp.content' - | 'snapshotRestoreApp.deleteRepositoryButton' - | 'snapshotRestoreApp.detailTitle' - | 'snapshotRestoreApp.documentationLink' - | 'snapshotRestoreApp.duration' - | 'snapshotRestoreApp.editRepositoryButton' - | 'snapshotRestoreApp.endTime' - | 'snapshotRestoreApp.euiFlyoutCloseButton' - | 'snapshotRestoreApp.includeGlobalState' - | 'snapshotRestoreApp.indices' - | 'snapshotRestoreApp.registerRepositoryButton' - | 'snapshotRestoreApp.reloadButton' - | 'snapshotRestoreApp.repositoryDetail' - | 'snapshotRestoreApp.repositoryLink' - | 'snapshotRestoreApp.repositoryList' - | 'snapshotRestoreApp.repositoryTable' - | 'snapshotRestoreApp.repositoryType' - | 'snapshotRestoreApp.row' - | 'snapshotRestoreApp.snapshotCount' - | 'snapshotRestoreApp.snapshotDetail' - | 'snapshotRestoreApp.snapshotLink' - | 'snapshotRestoreApp.snapshotList' - | 'snapshotRestoreApp.snapshotTable' - | 'snapshotRestoreApp.srRepositoryDetailsDeleteActionButton' - | 'snapshotRestoreApp.srRepositoryDetailsFlyoutCloseButton' - | 'snapshotRestoreApp.startTime' - | 'snapshotRestoreApp.state' - | 'snapshotRestoreApp.tab' - | 'snapshotRestoreApp.tableHeaderCell_durationInMillis_3' - | 'snapshotRestoreApp.tableHeaderCell_indices_4' - | 'snapshotRestoreApp.tableHeaderCell_name_0' - | 'snapshotRestoreApp.tableHeaderCell_repository_1' - | 'snapshotRestoreApp.tableHeaderCell_snapshot_0' - | 'snapshotRestoreApp.tableHeaderCell_startTimeInMillis_2' - | 'snapshotRestoreApp.tableHeaderCell_type_1' - | 'snapshotRestoreApp.tableHeaderSortButton' - | 'snapshotRestoreApp.title' - | 'snapshotRestoreApp.uuid' - | 'snapshotRestoreApp.value' - | 'snapshotRestoreApp.verifyRepositoryButton' - | 'snapshotRestoreApp.version' - | 'snapshotTable' - | 'snapshotTable.cell' - | 'snapshotTable.repositoryLink' - | 'snapshotTable.row' - | 'snapshotTable.snapshotLink' - | 'snapshotTable.tableHeaderCell_durationInMillis_3' - | 'snapshotTable.tableHeaderCell_indices_4' - | 'snapshotTable.tableHeaderCell_repository_1' - | 'snapshotTable.tableHeaderCell_snapshot_0' - | 'snapshotTable.tableHeaderCell_startTimeInMillis_2' - | 'snapshotTable.tableHeaderSortButton' - | 'srRepositoryDetailsDeleteActionButton' - | 'srRepositoryDetailsFlyoutCloseButton' - | 'startTime' - | 'startTime.title' - | 'startTime.value' - | 'state' - | 'state.title' - | 'state.value' - | 'repositories_tab' - | 'snapshots_tab' - | 'policies_tab' - | 'restore_status_tab' - | 'tableHeaderCell_durationInMillis_3' - | 'tableHeaderCell_durationInMillis_3.tableHeaderSortButton' - | 'tableHeaderCell_indices_4' - | 'tableHeaderCell_indices_4.tableHeaderSortButton' - | 'tableHeaderCell_name_0' - | 'tableHeaderCell_name_0.tableHeaderSortButton' - | 'tableHeaderCell_repository_1' - | 'tableHeaderCell_repository_1.tableHeaderSortButton' - | 'tableHeaderCell_shards.failed_6' - | 'tableHeaderCell_shards.total_5' - | 'tableHeaderCell_snapshot_0' - | 'tableHeaderCell_snapshot_0.tableHeaderSortButton' - | 'tableHeaderCell_startTimeInMillis_2' - | 'tableHeaderCell_startTimeInMillis_2.tableHeaderSortButton' - | 'tableHeaderCell_type_1' - | 'tableHeaderCell_type_1.tableHeaderSortButton' - | 'tableHeaderSortButton' - | 'title' - | 'uuid' - | 'uuid.title' - | 'uuid.value' - | 'value' - | 'verifyRepositoryButton' - | 'version' - | 'version.title' - | 'version.value' - | 'maxSnapshotsWarning' - | 'repositoryErrorsWarning' - | 'repositoryErrorsPrompt'; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts index 605265f7311ba6..662c50a98bfe83 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts @@ -92,11 +92,12 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { const setCleanupRepositoryResponse = (response?: HttpResponse, error?: any) => { const status = error ? error.status || 503 : 200; + const body = error ? JSON.stringify(error) : JSON.stringify(response); server.respondWith('POST', `${API_BASE_PATH}repositories/:name/cleanup`, [ status, { 'Content-Type': 'application/json' }, - JSON.stringify(response), + body, ]); }; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts index 071868e23f7fe7..7338e84f5c095f 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts @@ -372,6 +372,60 @@ describe('', () => { expect(latestRequest.method).toBe('GET'); expect(latestRequest.url).toBe(`${API_BASE_PATH}repositories/${repo1.name}/verify`); }); + + describe('clean repository', () => { + test('shows results when request succeeds', async () => { + httpRequestsMockHelpers.setCleanupRepositoryResponse({ + cleanup: { + cleaned: true, + response: { + results: { + deleted_bytes: 0, + deleted_blobs: 0, + }, + }, + }, + }); + + const { exists, find, component } = testBed; + await act(async () => { + find('repositoryDetail.cleanupRepositoryButton').simulate('click'); + }); + component.update(); + + const latestRequest = server.requests[server.requests.length - 1]; + expect(latestRequest.method).toBe('POST'); + expect(latestRequest.url).toBe(`${API_BASE_PATH}repositories/${repo1.name}/cleanup`); + + expect(exists('repositoryDetail.cleanupCodeBlock')).toBe(true); + expect(exists('repositoryDetail.cleanupError')).toBe(false); + }); + + test('shows error when success fails', async () => { + httpRequestsMockHelpers.setCleanupRepositoryResponse({ + cleanup: { + cleaned: false, + error: { + message: 'Error message', + statusCode: 400, + }, + }, + }); + + const { exists, find, component } = testBed; + await act(async () => { + find('repositoryDetail.cleanupRepositoryButton').simulate('click'); + }); + component.update(); + + const latestRequest = server.requests[server.requests.length - 1]; + expect(latestRequest.method).toBe('POST'); + expect(latestRequest.url).toBe(`${API_BASE_PATH}repositories/${repo1.name}/cleanup`); + + expect(exists('repositoryDetail.cleanupCodeBlock')).toBe(false); + expect(exists('repositoryDetail.cleanupError')).toBe(true); + }); + }); }); describe('when the repository has been fetched (and has snapshots)', () => { diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx index 91d01d44e093db..41054fe3e6033f 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx @@ -356,21 +356,16 @@ export const RepositoryDetails: React.FunctionComponent = ({ ) : ( - -

- {cleanup.error - ? JSON.stringify(cleanup.error) - : i18n.translate('xpack.snapshotRestore.repositoryDetails.cleanupUnknownError', { - defaultMessage: '503: Unknown error', - })} -

-
+ + } + error={cleanup.error as Error} + data-test-subj="cleanupError" + /> )} ) : null} diff --git a/x-pack/plugins/snapshot_restore/server/lib/wrap_es_error.ts b/x-pack/plugins/snapshot_restore/server/lib/wrap_es_error.ts index 6151aa9ef4a952..750e53222be03f 100644 --- a/x-pack/plugins/snapshot_restore/server/lib/wrap_es_error.ts +++ b/x-pack/plugins/snapshot_restore/server/lib/wrap_es_error.ts @@ -34,7 +34,7 @@ export const wrapEsError = (err: any, statusCodeToMessageMap: any = {}) => { root_cause = [], // eslint-disable-line @typescript-eslint/naming-convention caused_by = {}, // eslint-disable-line @typescript-eslint/naming-convention } = {}, - } = JSON.parse(response); + } = typeof response === 'string' ? JSON.parse(response) : response; // If no custom message if specified for the error's status code, just // wrap the error as a Boom error response, include the additional information from ES, and return it diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts index c220d922808229..e700c6bf9e04eb 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts @@ -233,12 +233,21 @@ export function registerRepositoriesRoutes({ try { const { body: cleanupResults } = await clusterClient.asCurrentUser.snapshot .cleanupRepository({ name }) - .catch((e) => ({ - body: { - cleaned: false, - error: e.response ? JSON.parse(e.response) : e, - }, - })); + .catch((e) => { + // This API returns errors in a non-standard format, which we'll need to + // munge to be compatible with wrapEsError. + const normalizedError = { + statusCode: e.meta.body.status, + response: e.meta.body, + }; + + return { + body: { + cleaned: false, + error: wrapEsError(normalizedError), + }, + }; + }); return res.ok({ body: { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a7b734ba206629..8bc0cef3a44e5b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -23213,7 +23213,6 @@ "xpack.snapshotRestore.repositoryDetails.cleanupErrorTitle": "申し訳ありません。リポジトリのクリーンアップ中にエラーが発生しました。", "xpack.snapshotRestore.repositoryDetails.cleanupRepositoryMessage": "スナップショットから参照されていないデータを削除するには、リポジトリをクリーンアップすることができます。これにより、ストレージ領域を解放できる場合があります。注:定期的にスナップショットを削除する場合は、この機能の利点が得られない可能性が高いため、使用頻度を低くしてください。", "xpack.snapshotRestore.repositoryDetails.cleanupTitle": "リポジトリのクリーンアップ", - "xpack.snapshotRestore.repositoryDetails.cleanupUnknownError": "503:不明なエラー", "xpack.snapshotRestore.repositoryDetails.closeButtonLabel": "閉じる", "xpack.snapshotRestore.repositoryDetails.editButtonLabel": "編集", "xpack.snapshotRestore.repositoryDetails.genericSettingsDescription": "レポジトリ「{name}」のランダムな設定です", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f5b364b35fa13c..d3de9dab0df526 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -23602,7 +23602,6 @@ "xpack.snapshotRestore.repositoryDetails.cleanupErrorTitle": "抱歉,清理存储库时出错。", "xpack.snapshotRestore.repositoryDetails.cleanupRepositoryMessage": "您可以清理存储库,以从快照中删除任何未引用的数据。这可节省存储空间。注意:如果定时删除快照,此功能可能并不那么有用,不应频繁使用。", "xpack.snapshotRestore.repositoryDetails.cleanupTitle": "存储库清理", - "xpack.snapshotRestore.repositoryDetails.cleanupUnknownError": "503:未知错误", "xpack.snapshotRestore.repositoryDetails.closeButtonLabel": "关闭", "xpack.snapshotRestore.repositoryDetails.editButtonLabel": "编辑", "xpack.snapshotRestore.repositoryDetails.genericSettingsDescription": "存储库“{name}”的只读设置",