From 5676c8fc949957e2297b86445406abf28892097a Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 28 Aug 2023 13:09:19 -0400 Subject: [PATCH 1/6] init commit: add index stats tab --- .../index_list/details_page/details_page.tsx | 10 ++ .../index_list/details_page/tabs/index.ts | 8 + .../index_list/details_page/tabs/stats.tsx | 138 ++++++++++++++++++ .../public/application/services/api.ts | 8 + .../public/application/services/index.ts | 1 + 5 files changed, 165 insertions(+) create mode 100644 x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/index.ts create mode 100644 x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx index 6d91b3a7991a1..d317c1284d7fd 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx @@ -24,6 +24,7 @@ import { DiscoverLink } from '../../../../lib/discover_link'; import { Section } from '../../home'; import { DetailsPageError } from './details_page_error'; import { ManageIndexButton } from './manage_index_button'; +import { StatsTab } from './tabs'; export enum IndexDetailsSection { Overview = 'overview', @@ -31,6 +32,7 @@ export enum IndexDetailsSection { Mappings = 'mappings', Settings = 'settings', Pipelines = 'pipelines', + Stats = 'stats', } const tabs = [ { @@ -63,6 +65,10 @@ const tabs = [ ), }, + { + id: IndexDetailsSection.Stats, + name: , + }, ]; export const DetailsPage: React.FunctionComponent< RouteComponentProps<{ indexName: string; indexDetailsSection: IndexDetailsSection }> @@ -186,6 +192,10 @@ export const DetailsPage: React.FunctionComponent< path={`/${Section.Indices}/${indexName}/${IndexDetailsSection.Pipelines}`} render={() =>
Pipelines
} /> + } + /> = ({ indexName }) => { + const { data: indexStats, isLoading, error, resendRequest } = useLoadIndexStats(indexName); + + if (isLoading) { + return ( + + + + ); + } + + if (error) { + return ( + + + + } + body={ + <> + + + + + + + + + } + /> + ); + } + + if (indexStats) { + return ( + + + + + {JSON.stringify(indexStats, null, 2)} + + + + + + + + + + + + + +

+ +

+
+
+
+ + +

+ primaries, + totalField: total, + }} + /> +

+
+
+
+
+ ); + } + + return null; +}; 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 b19382364722c..7db7d617ab9ff 100644 --- a/x-pack/plugins/index_management/public/application/services/api.ts +++ b/x-pack/plugins/index_management/public/application/services/api.ts @@ -6,6 +6,7 @@ */ import { METRIC_TYPE } from '@kbn/analytics'; +import { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; import { API_BASE_PATH, UIM_UPDATE_SETTINGS, @@ -319,3 +320,10 @@ export function loadIndex(indexName: string) { method: 'get', }); } + +export function useLoadIndexStats(indexName: string) { + return useRequest({ + path: `${API_BASE_PATH}/stats/${encodeURIComponent(indexName)}`, + method: 'get', + }); +} diff --git a/x-pack/plugins/index_management/public/application/services/index.ts b/x-pack/plugins/index_management/public/application/services/index.ts index 5cb65c04b6c9d..53fabecdd8080 100644 --- a/x-pack/plugins/index_management/public/application/services/index.ts +++ b/x-pack/plugins/index_management/public/application/services/index.ts @@ -25,6 +25,7 @@ export { simulateIndexTemplate, useLoadNodesPlugins, loadIndex, + useLoadIndexStats, } from './api'; export { sortTable } from './sort_table'; From 3de63d43a4f3db00d7513a5353acf99f5ad923f9 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 28 Aug 2023 13:31:44 -0400 Subject: [PATCH 2/6] add config check --- .../index_list/details_page/details_page.tsx | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx index d317c1284d7fd..a9d7a6674f16f 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx @@ -20,6 +20,7 @@ import { SectionLoading } from '@kbn/es-ui-shared-plugin/public'; import { Index } from '../../../../../../common'; import { loadIndex } from '../../../../services'; +import { useAppContext } from '../../../../app_context'; import { DiscoverLink } from '../../../../lib/discover_link'; import { Section } from '../../home'; import { DetailsPageError } from './details_page_error'; @@ -34,7 +35,7 @@ export enum IndexDetailsSection { Pipelines = 'pipelines', Stats = 'stats', } -const tabs = [ +const defaultTabs = [ { id: IndexDetailsSection.Overview, name: ( @@ -65,11 +66,13 @@ const tabs = [ ), }, - { - id: IndexDetailsSection.Stats, - name: , - }, ]; + +const statsTab = { + id: IndexDetailsSection.Stats, + name: , +}; + export const DetailsPage: React.FunctionComponent< RouteComponentProps<{ indexName: string; indexDetailsSection: IndexDetailsSection }> > = ({ @@ -78,6 +81,7 @@ export const DetailsPage: React.FunctionComponent< }, history, }) => { + const { config } = useAppContext(); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(); const [index, setIndex] = useState(); @@ -111,14 +115,16 @@ export const DetailsPage: React.FunctionComponent< }, [history]); const headerTabs = useMemo(() => { - return tabs.map((tab) => ({ + const visibleTabs = config.enableIndexStats ? [...defaultTabs, statsTab] : defaultTabs; + + return visibleTabs.map((tab) => ({ onClick: () => onSectionChange(tab.id), isSelected: tab.id === indexDetailsSection, key: tab.id, 'data-test-subj': `indexDetailsTab-${tab.id}`, label: tab.name, })); - }, [indexDetailsSection, onSectionChange]); + }, [indexDetailsSection, onSectionChange, config]); if (isLoading && !index) { return ( @@ -192,21 +198,18 @@ export const DetailsPage: React.FunctionComponent< path={`/${Section.Indices}/${indexName}/${IndexDetailsSection.Pipelines}`} render={() =>
Pipelines
} /> - } - /> + {config.enableIndexStats && ( + } + /> + )} - - -
-
{JSON.stringify(index, null, 2)}
-
); }; From 93cfcd9cf5e9028ec630a942896a9918bce685d9 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 28 Aug 2023 14:22:41 -0400 Subject: [PATCH 3/6] add tests --- .../index_details_page.helpers.ts | 14 ++++++++ .../index_details_page.test.ts | 33 +++++++++++++++++++ .../index_list/details_page/tabs/stats.tsx | 4 +-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts index 43d436c495799..9844d2ccdbf66 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts @@ -51,6 +51,10 @@ export interface IndexDetailsPageTestBed extends TestBed { isDisplayed: () => boolean; clickReloadButton: () => Promise; }; + statsTab: { + indexStatsContentExists: () => boolean; + indexStatsTabExists: () => boolean; + }; }; } @@ -135,6 +139,15 @@ export const setup = async ( component.update(); }, }; + + const statsTab = { + indexStatsContentExists: () => { + return exists('statsTabContent'); + }, + indexStatsTabExists: () => { + return exists('indexDetailsTab-stats'); + }, + }; return { ...testBed, routerMock, @@ -146,6 +159,7 @@ export const setup = async ( discoverLinkExists, contextMenu, errorSection, + statsTab, }, }; }; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts index 7b144fd0bad40..e0ad2444b69fb 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts @@ -60,6 +60,39 @@ describe('', () => { }); }); + describe('Stats tab', () => { + it('loads index stats from the API', async () => { + const numberOfRequests = 1; + // Expect initial request to fetch index details + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats); + expect(httpSetup.get).toHaveBeenLastCalledWith(`${API_BASE_PATH}/stats/${testIndexName}`, { + asSystemRequest: undefined, + body: undefined, + query: undefined, + version: undefined, + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); + + it('renders index stats', async () => { + await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats); + expect(testBed.actions.statsTab.indexStatsContentExists()).toBe(true); + }); + + it('hides index stats tab if enableIndexStats===false', async () => { + await act(async () => { + testBed = await setup(httpSetup, { + config: { enableIndexStats: false }, + }); + }); + testBed.component.update(); + + expect(testBed.actions.statsTab.indexStatsTabExists()).toBe(false); + }); + }); + it('loads index details from the API', async () => { expect(httpSetup.get).toHaveBeenLastCalledWith( `${INTERNAL_API_BASE_PATH}/indices/${testIndexName}`, diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx index aa35cb281e5f1..690d509d94cc7 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx @@ -88,7 +88,7 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => { if (indexStats) { return ( - + @@ -120,7 +120,7 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => {

primaries, totalField: total, From 6f28be441972248b4103ce3e07c770832a6d62bd Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Tue, 29 Aug 2023 11:15:23 -0400 Subject: [PATCH 4/6] code cleanup --- packages/kbn-doc-links/src/get_doc_links.ts | 1 + packages/kbn-doc-links/src/types.ts | 1 + .../index_list/details_page/details_page.tsx | 15 ++- .../index_list/details_page/tabs/index.ts | 2 +- .../index_list/details_page/tabs/stats.tsx | 91 ++++++++++++++----- .../application/services/documentation.ts | 6 ++ .../public/application/services/index.ts | 1 + 7 files changed, 87 insertions(+), 30 deletions(-) diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 1b1973e35ec52..a8d63137fa0a9 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -622,6 +622,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { }, apis: { bulkIndexAlias: `${ELASTICSEARCH_DOCS}indices-aliases.html`, + indexStats: `${ELASTICSEARCH_DOCS}indices-stats.html`, byteSizeUnits: `${ELASTICSEARCH_DOCS}api-conventions.html#byte-units`, createAutoFollowPattern: `${ELASTICSEARCH_DOCS}ccr-put-auto-follow-pattern.html`, createFollower: `${ELASTICSEARCH_DOCS}ccr-put-follow.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index b29017f71d739..428ef86267dd1 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -361,6 +361,7 @@ export interface DocLinks { readonly visualize: Record; readonly apis: Readonly<{ bulkIndexAlias: string; + indexStats: string; byteSizeUnits: string; createAutoFollowPattern: string; createFollower: string; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx index a9d7a6674f16f..c581d84cb9fe1 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx @@ -6,6 +6,7 @@ */ import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { css } from '@emotion/react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; import { Route, Routes } from '@kbn/shared-ux-router'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -25,7 +26,7 @@ import { DiscoverLink } from '../../../../lib/discover_link'; import { Section } from '../../home'; import { DetailsPageError } from './details_page_error'; import { ManageIndexButton } from './manage_index_button'; -import { StatsTab } from './tabs'; +import { DetailsPageStats } from './tabs'; export enum IndexDetailsSection { Overview = 'overview', @@ -139,7 +140,6 @@ export const DetailsPage: React.FunctionComponent< if (error || !index) { return ; } - return ( <> @@ -176,7 +176,12 @@ export const DetailsPage: React.FunctionComponent< -

+
{config.enableIndexStats && ( } + path={`/${Section.Indices}/:indexName/${IndexDetailsSection.Stats}`} + component={DetailsPageStats} /> )} = ({ indexName }) => { +export const DetailsPageStats: FunctionComponent> = ({ + match: { + params: { indexName }, + }, +}) => { const { data: indexStats, isLoading, error, resendRequest } = useLoadIndexStats(indexName); if (isLoading) { return ( @@ -51,7 +54,7 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => { title={

@@ -60,7 +63,7 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => { <> = ({ indexName }) => { data-test-subj="reloadIndexStatsButton" > @@ -87,18 +90,23 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => { } if (indexStats) { + // using "rowReverse" to keep docs links on the top of the stats code block on smaller screen return ( - - - - - {JSON.stringify(indexStats, null, 2)} - - - - - - + + + @@ -108,7 +116,7 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => {

@@ -116,10 +124,12 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => {
+ +

primaries, @@ -128,6 +138,39 @@ export const StatsTab: React.FunctionComponent = ({ indexName }) => { />

+ + + + + +
+
+ + + + + {JSON.stringify(indexStats, null, 2)} +
diff --git a/x-pack/plugins/index_management/public/application/services/documentation.ts b/x-pack/plugins/index_management/public/application/services/documentation.ts index 0eb9f62fffd09..3fc34477cacae 100644 --- a/x-pack/plugins/index_management/public/application/services/documentation.ts +++ b/x-pack/plugins/index_management/public/application/services/documentation.ts @@ -59,6 +59,7 @@ class DocumentationService { private runtimeFields: string = ''; private indicesComponentTemplate: string = ''; private bulkIndexAlias: string = ''; + private indexStats: string = ''; public setup(docLinks: DocLinksStart): void { const { links } = docLinks; @@ -111,6 +112,7 @@ class DocumentationService { this.runtimeFields = links.runtimeFields.overview; this.indicesComponentTemplate = links.apis.putComponentTemplate; this.bulkIndexAlias = links.apis.bulkIndexAlias; + this.indexStats = links.apis.indexStats; } public getEsDocsBase() { @@ -311,6 +313,10 @@ class DocumentationService { return this.bulkIndexAlias; } + public getIndexStats() { + return this.indexStats; + } + public getWellKnownTextLink() { return 'http://docs.opengeospatial.org/is/12-063r5/12-063r5.html'; } diff --git a/x-pack/plugins/index_management/public/application/services/index.ts b/x-pack/plugins/index_management/public/application/services/index.ts index 53fabecdd8080..7375cf27db653 100644 --- a/x-pack/plugins/index_management/public/application/services/index.ts +++ b/x-pack/plugins/index_management/public/application/services/index.ts @@ -33,3 +33,4 @@ export { sortTable } from './sort_table'; export { UiMetricService } from './ui_metric'; export { HttpService } from './http'; export { NotificationService } from './notification'; +export { documentationService } from './documentation'; From 944fb87b0a8079c42f3564777c32392b57a17387 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Tue, 29 Aug 2023 11:43:08 -0400 Subject: [PATCH 5/6] clean up tests --- .../helpers/http_requests.ts | 7 ++- .../index_details_page.helpers.ts | 29 ++++++++--- .../index_details_page.test.ts | 49 ++++++++++++++++--- .../index_details_page/mocks.ts | 26 ++++++++++ .../index_list/details_page/tabs/stats.tsx | 3 +- 5 files changed, 96 insertions(+), 18 deletions(-) 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 0a054fccdfcc1..9824e614742f3 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 @@ -100,8 +100,11 @@ const registerHttpRequestMockHelpers = ( const setLoadIndexMappingResponse = (response?: HttpResponse, error?: ResponseError) => mockResponse('GET', `${API_BASE_PATH}/mapping/:name`, response, error); - const setLoadIndexStatsResponse = (response?: HttpResponse, error?: ResponseError) => - mockResponse('GET', `${API_BASE_PATH}/stats/:name`, response, error); + const setLoadIndexStatsResponse = ( + indexName: string, + response?: HttpResponse, + error?: ResponseError + ) => mockResponse('GET', `${API_BASE_PATH}/stats/${indexName}`, response, error); const setUpdateIndexSettingsResponse = ( indexName: string, diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts index 9844d2ccdbf66..3f247144abf39 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts @@ -51,8 +51,11 @@ export interface IndexDetailsPageTestBed extends TestBed { isDisplayed: () => boolean; clickReloadButton: () => Promise; }; - statsTab: { - indexStatsContentExists: () => boolean; + stats: { + getCodeBlockContent: () => string; + getDocsLinkHref: () => string; + isErrorDisplayed: () => boolean; + clickErrorReloadButton: () => Promise; indexStatsTabExists: () => boolean; }; }; @@ -140,13 +143,25 @@ export const setup = async ( }, }; - const statsTab = { - indexStatsContentExists: () => { - return exists('statsTabContent'); - }, + const stats = { indexStatsTabExists: () => { return exists('indexDetailsTab-stats'); }, + getCodeBlockContent: () => { + return find('indexDetailsStatsCodeBlock').text(); + }, + getDocsLinkHref: () => { + return find('indexDetailsStatsDocsLink').prop('href'); + }, + isErrorDisplayed: () => { + return exists('indexDetailsStatsError'); + }, + clickErrorReloadButton: async () => { + await act(async () => { + find('reloadIndexStatsButton').simulate('click'); + }); + component.update(); + }, }; return { ...testBed, @@ -159,7 +174,7 @@ export const setup = async ( discoverLinkExists, contextMenu, errorSection, - statsTab, + stats, }, }; }; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts index e0ad2444b69fb..357ec6fbe160d 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts @@ -9,7 +9,7 @@ import { setupEnvironment } from '../helpers'; import { IndexDetailsPageTestBed, setup } from './index_details_page.helpers'; import { act } from 'react-dom/test-utils'; import { IndexDetailsSection } from '../../../public/application/sections/home/index_list/details_page'; -import { testIndexMock, testIndexName } from './mocks'; +import { testIndexMock, testIndexName, testIndexStats } from './mocks'; import { API_BASE_PATH, INTERNAL_API_BASE_PATH } from '../../../common'; describe('', () => { @@ -22,6 +22,7 @@ describe('', () => { ({ httpSetup, httpRequestsMockHelpers } = mockEnvironment); // testIndexName is configured in initialEntries of the memory router httpRequestsMockHelpers.setLoadIndexDetailsResponse(testIndexName, testIndexMock); + httpRequestsMockHelpers.setLoadIndexStatsResponse(testIndexName, testIndexStats); await act(async () => { testBed = await setup(httpSetup, { @@ -62,10 +63,6 @@ describe('', () => { describe('Stats tab', () => { it('loads index stats from the API', async () => { - const numberOfRequests = 1; - // Expect initial request to fetch index details - expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); - await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats); expect(httpSetup.get).toHaveBeenLastCalledWith(`${API_BASE_PATH}/stats/${testIndexName}`, { asSystemRequest: undefined, @@ -73,12 +70,21 @@ describe('', () => { query: undefined, version: undefined, }); - expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); }); it('renders index stats', async () => { await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats); - expect(testBed.actions.statsTab.indexStatsContentExists()).toBe(true); + const tabContent = testBed.actions.stats.getCodeBlockContent(); + expect(tabContent).toEqual(JSON.stringify(testIndexStats, null, 2)); + }); + + it('sets the docs link href from the documenation service', async () => { + await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats); + const docsLinkHref = testBed.actions.stats.getDocsLinkHref(); + // the url from the mocked docs mock + expect(docsLinkHref).toEqual( + 'https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/indices-stats.html' + ); }); it('hides index stats tab if enableIndexStats===false', async () => { @@ -89,7 +95,34 @@ describe('', () => { }); testBed.component.update(); - expect(testBed.actions.statsTab.indexStatsTabExists()).toBe(false); + expect(testBed.actions.stats.indexStatsTabExists()).toBe(false); + }); + + describe('Error handling', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadIndexStatsResponse(testIndexName, undefined, { + statusCode: 500, + message: 'Error', + }); + await act(async () => { + testBed = await setup(httpSetup); + }); + + testBed.component.update(); + await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats); + }); + + it('there is an error prompt', async () => { + expect(testBed.actions.stats.isErrorDisplayed()).toBe(true); + }); + + it('resends a request when reload button is clicked', async () => { + // already sent 3 requests while setting up the component + const numberOfRequests = 3; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + await testBed.actions.stats.clickErrorReloadButton(); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); }); }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts index 5e165fe0702e6..2dd93156a76ee 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts @@ -29,3 +29,29 @@ export const testIndexMock: Index = { }, isFollowerIndex: false, }; + +// Mocking partial index stats response +export const testIndexStats = { + _shards: { + total: 1, + successful: 1, + failed: 0, + }, + stats: { + uuid: 'tQ-n6sriQzC84xn58VYONQ', + health: 'green', + status: 'open', + primaries: { + docs: { + count: 1000, + deleted: 0, + }, + }, + total: { + docs: { + count: 1000, + deleted: 0, + }, + }, + }, +}; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx index ac48ace6b85df..ab8aa3e89d2b8 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/tabs/stats.tsx @@ -48,7 +48,7 @@ export const DetailsPageStats: FunctionComponent Date: Wed, 30 Aug 2023 10:52:05 -0400 Subject: [PATCH 6/6] show warning if index is closed --- .../index_details_page.helpers.ts | 4 ++ .../index_details_page.test.ts | 20 ++++++ .../index_list/details_page/details_page.tsx | 5 +- .../details_page/details_page_stats.tsx | 67 +++++++++++++++++-- .../public/application/services/api.ts | 4 +- .../public/application/services/index.ts | 2 +- 6 files changed, 92 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts index 50facaccf24be..cdf5e9f5ab841 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts @@ -63,6 +63,7 @@ export interface IndexDetailsPageTestBed extends TestBed { isErrorDisplayed: () => boolean; clickErrorReloadButton: () => Promise; indexStatsTabExists: () => boolean; + isWarningDisplayed: () => boolean; }; }; } @@ -180,6 +181,9 @@ export const setup = async ( isErrorDisplayed: () => { return exists('indexDetailsStatsError'); }, + isWarningDisplayed: () => { + return exists('indexStatsNotAvailableWarning'); + }, clickErrorReloadButton: async () => { await act(async () => { find('reloadIndexStatsButton').simulate('click'); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts index ffed9ae88aa64..ddd1d44ac9440 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts @@ -88,6 +88,26 @@ describe('', () => { ); }); + it('renders a warning message if an index is not open', async () => { + const testIndexMockWithClosedStatus = { + ...testIndexMock, + status: 'closed', + }; + + httpRequestsMockHelpers.setLoadIndexDetailsResponse( + testIndexName, + testIndexMockWithClosedStatus + ); + + await act(async () => { + testBed = await setup(httpSetup); + }); + testBed.component.update(); + + await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Stats); + expect(testBed.actions.stats.isWarningDisplayed()).toBe(true); + }); + it('hides index stats tab if enableIndexStats===false', async () => { await act(async () => { testBed = await setup(httpSetup, { diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx index 5f12ba5922bc5..80f79d9ab3c76 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx @@ -20,6 +20,7 @@ import { import { SectionLoading } from '@kbn/es-ui-shared-plugin/public'; import { Index } from '../../../../../../common'; +import { INDEX_OPEN } from '../../../../../../common/constants'; import { loadIndex } from '../../../../services'; import { useAppContext } from '../../../../app_context'; import { DiscoverLink } from '../../../../lib/discover_link'; @@ -207,7 +208,9 @@ export const DetailsPage: React.FunctionComponent< {config.enableIndexStats && ( ) => ( + + )} /> )} > = ({ +interface Props { + isIndexOpen: boolean; +} + +export const DetailsPageStats: FunctionComponent< + RouteComponentProps<{ indexName: string }> & Props +> = ({ match: { params: { indexName }, }, + isIndexOpen, }) => { - const { data: indexStats, isLoading, error, resendRequest } = useLoadIndexStats(indexName); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(); + const [indexStats, setIndexStats] = useState(); + + const fetchIndexStats = useCallback(async () => { + setIsLoading(true); + try { + const { data, error: loadingError } = await loadIndexStatistics(indexName); + setIsLoading(false); + setError(loadingError); + setIndexStats(data); + } catch (e) { + setIsLoading(false); + setError(e); + } + }, [indexName]); + + useEffect(() => { + if (isIndexOpen) { + fetchIndexStats(); + } + }, [fetchIndexStats, isIndexOpen]); + + if (isIndexOpen === false) { + return ( + + + + } + body={ +

+ +

+ } + /> + ); + } if (isLoading) { return ( @@ -64,7 +119,7 @@ export const DetailsPageStats: FunctionComponent ({ +export function loadIndexStatistics(indexName: string) { + return sendRequest({ path: `${API_BASE_PATH}/stats/${encodeURIComponent(indexName)}`, method: 'get', }); diff --git a/x-pack/plugins/index_management/public/application/services/index.ts b/x-pack/plugins/index_management/public/application/services/index.ts index ef16d66cb2950..5512171b358af 100644 --- a/x-pack/plugins/index_management/public/application/services/index.ts +++ b/x-pack/plugins/index_management/public/application/services/index.ts @@ -26,7 +26,7 @@ export { useLoadNodesPlugins, loadIndex, useLoadIndexMappings, - useLoadIndexStats, + loadIndexStatistics, } from './api'; export { sortTable } from './sort_table';