From 24ceaa6779cef7aef3248906837fc9cc234ade5f Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 12 Dec 2025 20:37:56 -0600 Subject: [PATCH 01/55] partial progress --- .../src/types.ts | 6 + .../public/ccr_data_enricher.ts | 35 ++++ .../public/plugin.ts | 4 +- .../server/plugin.ts | 53 +----- .../cross_cluster_replication/server/types.ts | 4 - .../public/index_lifecycle_data_enricher.ts | 27 +++ .../public/plugin.tsx | 2 + .../server/plugin.ts | 29 +--- .../server/types.ts | 2 - .../plugins/private/rollup/public/plugin.ts | 2 + .../rollup_data_enricher.ts | 6 +- .../plugins/private/rollup/server/plugin.ts | 7 +- .../plugins/private/rollup/server/types.ts | 2 - .../index_list/index_table/index_table.js | 2 + .../public/application/services/api.ts | 160 +++++++++++++++++- .../application/store/actions/load_indices.js | 1 + .../shared/index_management/public/plugin.ts | 8 + .../index_management/public/services/index.ts | 3 + .../services/index_data_enricher.ts | 11 +- .../shared/index_management/server/plugin.ts | 20 +-- .../server/routes/api/indices/indices_get.ts | 48 ++++++ .../routes/api/indices/indices_stats.ts | 37 ++++ .../routes/api/indices/metering_stats.ts | 35 ++++ .../api/indices/register_indices_routes.ts | 8 + .../index_management/server/services/index.ts | 9 - .../shared/index_management/server/types.ts | 2 - 26 files changed, 386 insertions(+), 137 deletions(-) create mode 100644 x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts create mode 100644 x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts rename x-pack/platform/plugins/private/rollup/{server => public}/rollup_data_enricher.ts (85%) rename x-pack/platform/plugins/shared/index_management/{server => public}/services/index_data_enricher.ts (76%) create mode 100644 x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts create mode 100644 x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts create mode 100644 x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts delete mode 100644 x-pack/platform/plugins/shared/index_management/server/services/index.ts diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index 8f60d20a2987c..92e4e9477a3b8 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -15,9 +15,12 @@ import type { ScopedHistory } from '@kbn/core-application-browser'; import type { SerializableRecord } from '@kbn/utility-types'; import type { LocatorPublic } from '@kbn/share-plugin/public'; import type { ManagementAppMountParams } from '@kbn/management-plugin/public'; +import type { HttpSetup } from '@kbn/core/public'; import type { ExtensionsSetup } from './services/extensions_service'; import type { PublicApiServiceSetup } from './services/public_api_service'; +export type Enricher = (indices: Index[], client: HttpSetup) => Promise; + export type IndexManagementLocatorParams = SerializableRecord & ( | { @@ -79,6 +82,9 @@ export interface IndexManagementPluginSetup { extensionsService: ExtensionsSetup; renderIndexManagementApp: (params: IndexManagementAppMountParams) => Promise<() => void>; locator?: IndexManagementLocator; + indexDataEnricher: { + add: (enricher: Enricher) => void; + }; } export interface IndexManagementPluginStart { diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts new file mode 100644 index 0000000000000..b716f0eff80c8 --- /dev/null +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts @@ -0,0 +1,35 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Index } from '@kbn/index-management-shared-types'; + +export const ccrDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => { + if (!indicesList?.length) { + return indicesList; + } + + try { + // to do need to make an endpoint for this + const { follower_indices: followerIndices } = await client.asCurrentUser.ccr.followInfo({ + index: '_all', + }); + + return indicesList.map((index) => { + const isFollowerIndex = !!followerIndices.find( + (followerIndex: { follower_index: string }) => { + return followerIndex.follower_index === index.name; + } + ); + return { + ...index, + isFollowerIndex, + }; + }); + } catch (e) { + return indicesList; + } +}; diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts index ad62564f24505..4a02fe0c12b3f 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts @@ -14,8 +14,8 @@ import { PLUGIN, MANAGEMENT_ID } from '../common/constants'; import { init as initUiMetric } from './app/services/track_ui_metric'; import { init as initNotification } from './app/services/notifications'; import type { PluginDependencies, ClientConfigType } from './types'; +import { ccrDataEnricher } from './ccr_data_enricher'; -// @ts-ignore; import { setHttpClient } from './app/services/api'; export class CrossClusterReplicationPlugin implements Plugin { @@ -105,6 +105,8 @@ export class CrossClusterReplicationPlugin implements Plugin { }; indexManagement.extensionsService.addBadge(followerBadgeExtension); + // note this isn't disabled if license changes + indexManagement.indexDataEnricher.add(ccrDataEnricher); } } else { ccrApp.disable(); diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts index 58099417c349f..d9c21dcb3f729 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts @@ -5,8 +5,6 @@ * 2.0. */ -import type { Observable } from 'rxjs'; -import { firstValueFrom } from 'rxjs'; import type { CoreSetup, CoreStart, @@ -14,71 +12,22 @@ import type { Logger, PluginInitializerContext, } from '@kbn/core/server'; -import type { IScopedClusterClient } from '@kbn/core/server'; -import type { Index } from '@kbn/index-management-plugin/server'; import { PLUGIN } from '../common/constants'; import type { SetupDependencies, StartDependencies } from './types'; import { registerApiRoutes } from './routes'; -import type { CrossClusterReplicationConfig } from './config'; import { License, handleEsError } from './shared_imports'; -const ccrDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => { - if (!indicesList?.length) { - return indicesList; - } - - try { - const { follower_indices: followerIndices } = await client.asCurrentUser.ccr.followInfo({ - index: '_all', - }); - - return indicesList.map((index) => { - const isFollowerIndex = !!followerIndices.find( - (followerIndex: { follower_index: string }) => { - return followerIndex.follower_index === index.name; - } - ); - return { - ...index, - isFollowerIndex, - }; - }); - } catch (e) { - return indicesList; - } -}; - export class CrossClusterReplicationServerPlugin implements Plugin { - private readonly config$: Observable; private readonly license: License; private readonly logger: Logger; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); - this.config$ = initializerContext.config.create(); this.license = new License(); } - setup( - { http, getStartServices }: CoreSetup, - { features, licensing, indexManagement, remoteClusters }: SetupDependencies - ) { - void firstValueFrom(this.config$).then((config) => { - // remoteClusters.isUiEnabled is driven by the xpack.remote_clusters.ui.enabled setting. - // The CCR UI depends upon the Remote Clusters UI (e.g. by cross-linking to it), so if - // the Remote Clusters UI is disabled we can't show the CCR UI. - const isCcrUiEnabled = config.ui.enabled && remoteClusters.isUiEnabled; - - // If the UI isn't enabled, then we don't want to expose any CCR concepts in the UI, including - // "follower" badges for follower indices. - if (isCcrUiEnabled) { - if (indexManagement.indexDataEnricher) { - indexManagement.indexDataEnricher.add(ccrDataEnricher); - } - } - }); - + setup({ http }: CoreSetup, { features }: SetupDependencies) { this.license.setup({ pluginName: PLUGIN.TITLE, logger: this.logger, diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts index a85dc733c30ce..e24a73f523baf 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts @@ -8,14 +8,10 @@ import type { IRouter } from '@kbn/core/server'; import type { FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/server'; -import type { IndexManagementPluginSetup } from '@kbn/index-management-plugin/server'; -import type { RemoteClustersPluginSetup } from '@kbn/remote-clusters-plugin/server'; import type { License, handleEsError } from './shared_imports'; export interface SetupDependencies { licensing: LicensingPluginSetup; - indexManagement: IndexManagementPluginSetup; - remoteClusters: RemoteClustersPluginSetup; features: FeaturesPluginSetup; } diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts new file mode 100644 index 0000000000000..abb0eb2160ba2 --- /dev/null +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts @@ -0,0 +1,27 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Index } from '@kbn/index-management-shared-types'; + +export const indexLifecycleDataEnricher = async ( + indicesList: Index[], + client: IScopedClusterClient +): Promise => { + if (!indicesList || !indicesList.length) { + return []; + } + + const { indices: ilmIndicesData } = await client.asCurrentUser.ilm.explainLifecycle({ + index: '*,.*', + }); + return indicesList.map((index: Index) => { + return { + ...index, + ilm: { ...(ilmIndicesData[index.name] || {}) }, + }; + }); +}; diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/plugin.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/public/plugin.tsx index c883e828bd5d2..d8e3d7270435c 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/plugin.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/plugin.tsx @@ -16,6 +16,7 @@ import { BreadcrumbService } from './application/services/breadcrumbs'; import { addAllExtensions } from './extend_index_management'; import type { ClientConfigType, SetupDependencies, StartDependencies } from './types'; import { IlmLocatorDefinition } from './locator'; +import { indexLifecycleDataEnricher } from './index_lifecycle_data_enricher'; export class IndexLifecycleManagementPlugin implements Plugin @@ -104,6 +105,7 @@ export class IndexLifecycleManagementPlugin if (indexManagement) { addAllExtensions(indexManagement.extensionsService); + indexManagement.indexDataEnricher.add(indexLifecycleDataEnricher); } plugins.share.url.locators.create( diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts b/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts index 1c54971eebed8..2926b4ae9f7d5 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts @@ -7,9 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { CoreSetup, Plugin, Logger, PluginInitializerContext } from '@kbn/core/server'; -import type { IScopedClusterClient } from '@kbn/core/server'; -import type { Index } from '@kbn/index-management-plugin/common/types'; import { PLUGIN } from '../common/constants'; import type { Dependencies } from './types'; import { registerApiRoutes } from './routes'; @@ -17,25 +15,6 @@ import { License } from './services'; import type { IndexLifecycleManagementConfig } from './config'; import { handleEsError } from './shared_imports'; -const indexLifecycleDataEnricher = async ( - indicesList: Index[], - client: IScopedClusterClient -): Promise => { - if (!indicesList || !indicesList.length) { - return []; - } - - const { indices: ilmIndicesData } = await client.asCurrentUser.ilm.explainLifecycle({ - index: '*,.*', - }); - return indicesList.map((index: Index) => { - return { - ...index, - ilm: { ...(ilmIndicesData[index.name] || {}) }, - }; - }); -}; - export class IndexLifecycleManagementServerPlugin implements Plugin { private readonly config: IndexLifecycleManagementConfig; private readonly license: License; @@ -47,7 +26,7 @@ export class IndexLifecycleManagementServerPlugin implements Plugin { +export const rollupDataEnricher = async (indicesList: Index[], client: HttpSetup) => { if (!indicesList || !indicesList.length) { return Promise.resolve(indicesList); } diff --git a/x-pack/platform/plugins/private/rollup/server/plugin.ts b/x-pack/platform/plugins/private/rollup/server/plugin.ts index 9d13bbc1f1553..c8cc03fb6c7d5 100644 --- a/x-pack/platform/plugins/private/rollup/server/plugin.ts +++ b/x-pack/platform/plugins/private/rollup/server/plugin.ts @@ -15,7 +15,6 @@ import type { Dependencies } from './types'; import { registerApiRoutes } from './routes'; import { License } from './services'; import { registerRollupUsageCollector } from './collectors'; -import { rollupDataEnricher } from './rollup_data_enricher'; import { IndexPatternsFetcher } from './shared_imports'; import { handleEsError } from './shared_imports'; import { formatEsError } from './lib/format_es_error'; @@ -34,7 +33,7 @@ export class RollupPlugin implements Plugin { public setup( { http, uiSettings, getStartServices }: CoreSetup, - { features, licensing, indexManagement, usageCollection, dataViews, data }: Dependencies + { features, licensing, usageCollection, dataViews, data }: Dependencies ) { this.license.setup( { @@ -110,10 +109,6 @@ export class RollupPlugin implements Plugin { } if (this.config.ui.enabled) { - if (indexManagement && indexManagement.indexDataEnricher) { - indexManagement.indexDataEnricher.add(rollupDataEnricher); - } - dataViews.enableRollups(); data.search.enableRollups(); } diff --git a/x-pack/platform/plugins/private/rollup/server/types.ts b/x-pack/platform/plugins/private/rollup/server/types.ts index 1d15cfa2189f6..a654b90a17cc5 100644 --- a/x-pack/platform/plugins/private/rollup/server/types.ts +++ b/x-pack/platform/plugins/private/rollup/server/types.ts @@ -9,7 +9,6 @@ import type { IRouter } from '@kbn/core/server'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import type { getCapabilitiesForRollupIndices } from '@kbn/data-views-plugin/server'; -import type { IndexManagementPluginSetup } from '@kbn/index-management-plugin/server'; import type { FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { DataViewsServerPluginSetup } from '@kbn/data-views-plugin/server'; import type { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; @@ -20,7 +19,6 @@ import type { handleEsError } from './shared_imports'; import type { formatEsError } from './lib/format_es_error'; export interface Dependencies { - indexManagement?: IndexManagementPluginSetup; usageCollection?: UsageCollectionSetup; licensing: LicensingPluginSetup; features: FeaturesPluginSetup; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index b1710e2089be8..a70814cc68278 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -535,6 +535,8 @@ export class IndexTable extends Component { ); } + console.log('indicesError', indicesError); + if (indicesError) { if (indicesError.status === 403) { return ( diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 3ad3c792bfd76..200f694da02f7 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -5,9 +5,13 @@ * 2.0. */ +import { ByteSizeValue } from '@kbn/config-schema'; import { METRIC_TYPE } from '@kbn/analytics'; import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; -import type { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; +import type { + IndicesStatsResponse, + IndicesGetResponse, +} from '@elastic/elasticsearch/lib/api/types'; import type { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; import type { ReindexService } from '@kbn/reindex-service-plugin/public'; @@ -53,6 +57,10 @@ interface ReloadIndicesOptions { asSystemRequest?: boolean; } +interface MeteringStatsResponse { + indices: MeteringStats[]; +} + // Temporary hack to provide the uiMetricService and reindexService instance to this file. // TODO: Refactor and export an ApiService instance through the app dependencies context let uiMetricService: UiMetricService; @@ -139,9 +147,155 @@ export async function updateDSFailureStore( }); } +interface IndexMetadata { + name: string; + primary?: Number; + replica?: Number; + isFrozen: boolean; + aliases: string[] | 'none'; + hidden: boolean; + data_stream: string; + mode: string; + health?: string; + status?: string; + uuid?: string; + documents?: number; + documents_deleted?: number; + size?: string; + primary_size?: string; +} + +const getBaseResponse = ({ + indexName, + indexData, + aliases, +}: { + indexName: string; + indexData: any; + aliases: string[]; +}): IndexMetadata => ({ + name: indexName, + primary: indexData.settings?.index?.number_of_shards as Number, + replica: indexData.settings?.index?.number_of_replicas as Number, + isFrozen: indexData.settings?.index?.frozen === 'true', + aliases: aliases.length ? aliases : 'none', + hidden: indexData.settings?.index?.hidden === 'true', + data_stream: indexData.data_stream as string, + mode: indexData.settings?.index?.mode as string, +}); + export async function loadIndices() { - const response = await httpService.httpClient.get(`${API_BASE_PATH}/indices`); - return response.data ? response.data : response; + const config = { + isIndexStatsEnabled: true, + isSizeAndDocCountEnabled: true, + // until endpoit is implemented + // isSizeAndDocCountEnabled: false, + }; + + const promises: [ + Promise, + Promise, + Promise + ][] = []; + + const getIndicesPromise = httpService.httpClient.get( + `${API_BASE_PATH}/indices_get` + ); + // @ts-expect-error + promises.push([getIndicesPromise]); + if (config.isIndexStatsEnabled) { + const getIndicesStatsPromise = httpService.httpClient.get( + `${API_BASE_PATH}/indices_stats` + ); + promises[0].push(getIndicesStatsPromise); + } else { + promises[0].push(Promise.resolve({})); + } + + // uses the _metering/stats API to get the number of documents and size of the index + // this API is only available in ES3 + if (config.isSizeAndDocCountEnabled) { + // todo + const meteringStatsPromise = httpService.httpClient + .get(`${API_BASE_PATH}/metering_stats`) + .catch(() => ({} as MeteringStatsResponse)); + + // todo why does this depend upon elevated privs? + /* + const getMeteringStatsPromise = + client.asSecondaryAuthUser.transport.request({ + method: 'GET', + path: `/_metering/stats/` + indexNamesString, + }); + */ + promises[0].push(meteringStatsPromise); + } else { + promises[0].push(Promise.resolve({})); + } + + // todo - this will run at speed of slowest promise + // would be better to run independently and combine later + const [indices, indicesStats, meteringStats] = await Promise.all(promises[0]); + + const indicesNames = Object.keys(indices); + + if (!indicesNames.length) { + return []; + } + + // todo better type + const mainBaseResponse = indicesNames.reduce>( + (collector, indexName: string) => { + const indexData = indices[indexName]; + const aliases = Object.keys(indexData.aliases!); + collector[indexName] = getBaseResponse({ + indexName, + indexData, + aliases, + }); + + return collector; + }, + {} + ); + + if (indicesStats?.indices) { + Object.values(mainBaseResponse).forEach((baseResponse) => { + const indexStats = indicesStats.indices![baseResponse.name] || {}; + + baseResponse.health = indexStats?.health; + baseResponse.status = indexStats?.status; + baseResponse.uuid = indexStats?.uuid; + baseResponse.documents = indexStats?.primaries?.docs?.count ?? 0; + baseResponse.documents_deleted = indexStats?.primaries?.docs?.deleted ?? 0; + baseResponse.size = new ByteSizeValue( + indexStats?.total?.store?.size_in_bytes ?? 0 + ).toString(); + baseResponse.primary_size = new ByteSizeValue( + indexStats?.primaries?.store?.size_in_bytes ?? 0 + ).toString(); + }); + } + + const meteringStatsWithTypes = meteringStats as MeteringStatsResponse | undefined; + + if (meteringStatsWithTypes?.indices?.length === 0) { + meteringStatsWithTypes?.indices?.forEach((indexStat) => { + const baseResponse = mainBaseResponse[indexStat.name]; + if (baseResponse) { + baseResponse.documents = indexStat.num_docs; + baseResponse.size = new ByteSizeValue(indexStat.size_in_bytes).toString(); + } + }); + } + + // todo change api + const enriched = await indexDataEnricher.enrichIndices(indices, httpService.httpClient); + + // if neither index stats (Stateful only API) + // nor size and doc count are enabled (ES3 only API) + // return the base response + return Object.values(mainBaseResponse); } export async function reloadIndices( diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js index f5a5679b02767..729c90a8e2010 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js @@ -18,6 +18,7 @@ export const loadIndices = () => async (dispatch) => { try { indices = await request(); } catch (error) { + console.log('loadIndices error', error); return dispatch(loadIndicesError(error)); } dispatch(loadIndicesSuccess({ indices })); diff --git a/x-pack/platform/plugins/shared/index_management/public/plugin.ts b/x-pack/platform/plugins/shared/index_management/public/plugin.ts index ce5f09115c43c..a686148e4fb5d 100644 --- a/x-pack/platform/plugins/shared/index_management/public/plugin.ts +++ b/x-pack/platform/plugins/shared/index_management/public/plugin.ts @@ -46,6 +46,7 @@ import { IndexManagementLocatorDefinition } from './locator'; import { ComponentTemplateFlyout } from './application/components/component_templates/component_templates_flyout_embeddable'; import { DataStreamFlyout } from './application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_flyout_embeddable'; import { IndexTemplateFlyout } from './application/sections/home/template_list/template_details/index_template_flyout_embeddable'; +import { IndexDataEnricher } from './services'; export class IndexMgmtUIPlugin implements @@ -79,6 +80,8 @@ export class IndexMgmtUIPlugin private capabilities$ = new Subject(); + private readonly indexDataEnricher: IndexDataEnricher; + constructor(ctx: PluginInitializerContext) { // Temporary hack to provide the service instances in module files in order to avoid a big refactor // For the selectors we should expose them through app dependencies and read them from there on each container component. @@ -113,6 +116,8 @@ export class IndexMgmtUIPlugin enforceAdaptiveAllocations: ctx.env.packageInfo.buildFlavor === 'serverless', enableFailureStoreRetentionDisabling: enableFailureStoreRetentionDisabling ?? true, }; + + this.indexDataEnricher = new IndexDataEnricher(); } public setup( @@ -178,6 +183,9 @@ export class IndexMgmtUIPlugin }); }, locator: this.locator, + indexDataEnricher: { + add: this.indexDataEnricher.add.bind(this.indexDataEnricher), + }, }; } diff --git a/x-pack/platform/plugins/shared/index_management/public/services/index.ts b/x-pack/platform/plugins/shared/index_management/public/services/index.ts index 92c6e95a8b432..e26aab7f58ca0 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/index.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index.ts @@ -8,3 +8,6 @@ export { ExtensionsService } from './extensions_service'; export { PublicApiService } from './public_api_service'; + +export { IndexDataEnricher } from './index_data_enricher'; +export type { Enricher } from '@kbn/index-management-shared-types'; diff --git a/x-pack/platform/plugins/shared/index_management/server/services/index_data_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts similarity index 76% rename from x-pack/platform/plugins/shared/index_management/server/services/index_data_enricher.ts rename to x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts index 7138b0ccc53f9..234a5e87a6c4c 100644 --- a/x-pack/platform/plugins/shared/index_management/server/services/index_data_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts @@ -5,11 +5,10 @@ * 2.0. */ -import type { IScopedClusterClient } from '@kbn/core/server'; +import type { HttpSetup } from '@kbn/core/public'; +import type { Enricher } from '@kbn/index-management-shared-types'; import type { Index } from '..'; -export type Enricher = (indices: Index[], client: IScopedClusterClient) => Promise; - export class IndexDataEnricher { private readonly _enrichers: Enricher[] = []; @@ -17,15 +16,13 @@ export class IndexDataEnricher { this._enrichers.push(enricher); } - public enrichIndices = async ( - indices: Index[], - client: IScopedClusterClient - ): Promise => { + public enrichIndices = async (indices: Index[], client: HttpSetup): Promise => { let enrichedIndices = indices; for (let i = 0; i < this.enrichers.length; i++) { const dataEnricher = this.enrichers[i]; try { + // todo do this in parallel const dataEnricherResponse = await dataEnricher(enrichedIndices, client); enrichedIndices = dataEnricherResponse; } catch (e) { diff --git a/x-pack/platform/plugins/shared/index_management/server/plugin.ts b/x-pack/platform/plugins/shared/index_management/server/plugin.ts index 638eda8e8f9de..529d18723d6bc 100644 --- a/x-pack/platform/plugins/shared/index_management/server/plugin.ts +++ b/x-pack/platform/plugins/shared/index_management/server/plugin.ts @@ -10,28 +10,19 @@ import type { CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/serv import { PLUGIN } from '../common/constants/plugin'; import type { Dependencies } from './types'; import { ApiRoutes } from './routes'; -import { IndexDataEnricher } from './services'; import { handleEsError } from './shared_imports'; import type { IndexManagementConfig } from './config'; -export interface IndexManagementPluginSetup { - indexDataEnricher: { - add: IndexDataEnricher['add']; - }; -} - -export class IndexMgmtServerPlugin implements Plugin { +export class IndexMgmtServerPlugin implements Plugin { private readonly apiRoutes: ApiRoutes; - private readonly indexDataEnricher: IndexDataEnricher; private readonly config: IndexManagementConfig; constructor(initContext: PluginInitializerContext) { this.apiRoutes = new ApiRoutes(); - this.indexDataEnricher = new IndexDataEnricher(); this.config = initContext.config.get(); } - setup({ http }: CoreSetup, { features, security }: Dependencies): IndexManagementPluginSetup { + setup({ http }: CoreSetup, { features, security }: Dependencies) { features.registerElasticsearchFeature({ id: PLUGIN.id, privileges: [ @@ -70,17 +61,10 @@ export class IndexMgmtServerPlugin implements Plugin { + const { client } = (await context.core).elasticsearch; + try { + const indices = await client.asCurrentUser.indices.get({ + index: '*', // indexNamesString, + expand_wildcards: ['hidden', 'all'], + // only get specified index properties from ES to keep the response under 536MB + // node.js string length limit: https://github.com/nodejs/node/issues/33960 + filter_path: [ + '*.aliases', + '*.settings.index.number_of_shards', + '*.settings.index.number_of_replicas', + '*.settings.index.frozen', + '*.settings.index.hidden', + '*.settings.index.mode', + '*.data_stream', + ], + // for better performance only compute aliases and settings of indices but not mappings + features: ['aliases', 'settings'], + }); + return response.ok({ body: indices }); + } catch (error) { + return handleEsError({ error, response }); + } + } + ); +} diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts new file mode 100644 index 0000000000000..b75dbd79f9967 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts @@ -0,0 +1,37 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { RouteDependencies } from '../../../types'; +import { addBasePath } from '..'; + +export function registerIndicesStats({ router, lib: { handleEsError } }: RouteDependencies) { + router.get( + { + path: addBasePath('/indices_stats'), + security: { + authz: { + enabled: false, + reason: 'Relies on es client for authorization', + }, + }, + validate: false, + }, + async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + try { + const { indices } = await client.asCurrentUser.indices.stats({ + index: '*', // indexNamesString, + expand_wildcards: ['hidden', 'all'], + forbid_closed_indices: false, + metric: ['docs', 'store'], + }); + return response.ok({ body: indices }); + } catch (error) { + return handleEsError({ error, response }); + } + } + ); +} diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts new file mode 100644 index 0000000000000..44ccb62d16ad1 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts @@ -0,0 +1,35 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { RouteDependencies } from '../../../types'; +import { addBasePath } from '..'; + +export function registerMeteringStats({ router, lib: { handleEsError } }: RouteDependencies) { + router.get( + { + path: addBasePath('/metering_stats'), + security: { + authz: { + enabled: false, + reason: 'Relies on es client for authorization', + }, + }, + validate: false, + }, + async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + try { + const data = await client.asSecondaryAuthUser.transport.request({ + method: 'GET', + path: `/_metering/stats/` + '*', // indexNamesString, + }); + return response.ok({ body: data }); + } catch (error) { + return handleEsError({ error, response }); + } + } + ); +} diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts index 16b62807630e4..4e7cd6332a037 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts @@ -19,6 +19,10 @@ import { registerDeleteRoute } from './register_delete_route'; import { registerGetRoute } from './register_get_route'; import { registerCreateRoute } from './register_create_route'; +import { registerIndicesGet } from './indices_get'; +import { registerIndicesStats } from './indices_stats'; +import { registerMeteringStats } from './metering_stats'; + export function registerIndicesRoutes(dependencies: RouteDependencies) { registerClearCacheRoute(dependencies); registerCloseRoute(dependencies); @@ -31,4 +35,8 @@ export function registerIndicesRoutes(dependencies: RouteDependencies) { registerDeleteRoute(dependencies); registerGetRoute(dependencies); registerCreateRoute(dependencies); + + registerIndicesGet(dependencies); + registerIndicesStats(dependencies); + registerMeteringStats(dependencies); } diff --git a/x-pack/platform/plugins/shared/index_management/server/services/index.ts b/x-pack/platform/plugins/shared/index_management/server/services/index.ts deleted file mode 100644 index bd62f2df80cc8..0000000000000 --- a/x-pack/platform/plugins/shared/index_management/server/services/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export type { Enricher } from './index_data_enricher'; -export { IndexDataEnricher } from './index_data_enricher'; diff --git a/x-pack/platform/plugins/shared/index_management/server/types.ts b/x-pack/platform/plugins/shared/index_management/server/types.ts index 227a219f70ce5..80ac1aefb4488 100644 --- a/x-pack/platform/plugins/shared/index_management/server/types.ts +++ b/x-pack/platform/plugins/shared/index_management/server/types.ts @@ -10,7 +10,6 @@ import type { IRouter } from '@kbn/core/server'; import type { FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; -import type { IndexDataEnricher } from './services'; import type { handleEsError } from './shared_imports'; export interface Dependencies { @@ -32,7 +31,6 @@ export interface RouteDependencies { enableProjectLevelRetentionChecks: boolean; enableFailureStoreRetentionDisabling: boolean; }; - indexDataEnricher: IndexDataEnricher; lib: { handleEsError: typeof handleEsError; }; From 9d730924a39c2dde71eb6bbbfe94cbf521f4b8f0 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 15 Dec 2025 21:58:25 -0600 Subject: [PATCH 02/55] ccr progress --- .../public/ccr_data_enricher.ts | 25 +++------- .../public/plugin.ts | 1 + .../server/routes/api/follower_index/index.ts | 2 + .../register_get_follower_info.ts | 49 +++++++++++++++++++ .../server/lib/fetch_indices.ts | 2 +- 5 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts index b716f0eff80c8..478fea7006c02 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts @@ -1,24 +1,18 @@ -/* +=/* * 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; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { Index } from '@kbn/index-management-shared-types'; - -export const ccrDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => { - if (!indicesList?.length) { - return indicesList; - } +import type { HttpSetup } from '@kbn/core/public'; +import type { CcrFollowInfoResponse } from '@elastic/elasticsearch/lib/api/types'; - try { - // to do need to make an endpoint for this - const { follower_indices: followerIndices } = await client.asCurrentUser.ccr.followInfo({ - index: '_all', - }); +export const ccrDataEnricher = async (client: HttpSetup) => client.get('/internal/ccr/follower_info'); - return indicesList.map((index) => { +/* +this is just setting a boolean whether its a follower index +indicesList.map((index) => { const isFollowerIndex = !!followerIndices.find( (followerIndex: { follower_index: string }) => { return followerIndex.follower_index === index.name; @@ -29,7 +23,4 @@ export const ccrDataEnricher = async (indicesList: Index[], client: IScopedClust isFollowerIndex, }; }); - } catch (e) { - return indicesList; - } -}; + */ diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts index 4a02fe0c12b3f..70f198b33d724 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts @@ -106,6 +106,7 @@ export class CrossClusterReplicationPlugin implements Plugin { indexManagement.extensionsService.addBadge(followerBadgeExtension); // note this isn't disabled if license changes + // todo need to fix indexManagement.indexDataEnricher.add(ccrDataEnricher); } } else { diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/index.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/index.ts index 62965856b617b..79230b1053c0b 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/index.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/index.ts @@ -13,6 +13,7 @@ import { registerPauseRoute } from './register_pause_route'; import { registerResumeRoute } from './register_resume_route'; import { registerUnfollowRoute } from './register_unfollow_route'; import { registerUpdateRoute } from './register_update_route'; +import { registerGetFollowerInfoRoute } from './register_get_follower_info'; export function registerFollowerIndexRoutes(dependencies: RouteDependencies) { registerCreateRoute(dependencies); @@ -22,4 +23,5 @@ export function registerFollowerIndexRoutes(dependencies: RouteDependencies) { registerResumeRoute(dependencies); registerUnfollowRoute(dependencies); registerUpdateRoute(dependencies); + registerGetFollowerInfoRoute(dependencies); } diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts new file mode 100644 index 0000000000000..2ab01145c7b3f --- /dev/null +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts @@ -0,0 +1,49 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { addBasePath } from '../../../services'; +import type { RouteDependencies } from '../../../types'; + +/** + * Returns a single follower index pattern + */ +export const registerGetFollowerInfoRoute = ({ + router, + license, + lib: { handleEsError }, +}: RouteDependencies) => { + router.get( + { + path: addBasePath('/follower_info'), + security: { + authz: { + enabled: false, + reason: 'Relies on es client for authorization', + }, + }, + validate: { + params: schema.never(), + }, + }, + license.guardApiRoute(async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + + try { + const body = await client.asCurrentUser.ccr.followInfo({ + index: '*', + }); + + return response.ok({ + body, + }); + } catch (error) { + return handleEsError({ error, response }); + } + }) + ); +}; diff --git a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts index 1c38e43ac344c..ec8f8541b447d 100644 --- a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts +++ b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts @@ -92,7 +92,7 @@ async function fetchIndicesCall( } // uses the _metering/stats API to get the number of documents and size of the index - // this API is only available in ES3 + // this API is only available in ES3 (serverless) if (config.isSizeAndDocCountEnabled) { const { indices: indicesStats } = await client.asSecondaryAuthUser.transport.request({ From e23108bc68dfdf6479fb1636648376baa311caff Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 15 Dec 2025 21:58:58 -0600 Subject: [PATCH 03/55] ccr progress --- .../public/index_lifecycle_data_enricher.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts index abb0eb2160ba2..6e02931560838 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts @@ -21,6 +21,7 @@ export const indexLifecycleDataEnricher = async ( return indicesList.map((index: Index) => { return { ...index, + // simply appends ilm data if it exists ilm: { ...(ilmIndicesData[index.name] || {}) }, }; }); From 8f3bcc21c3d8fefe03175596e1519139e1880b16 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 14 Jan 2026 01:15:16 -0600 Subject: [PATCH 04/55] partial progress Signed-off-by: Matthew Kime --- .../shared/index_management/server/plugin.ts | 20 ++++++++- .../routes/api/indices/metering_stats.ts | 35 ---------------- .../index_management/server/services/index.ts | 9 ++++ .../server/services/index_data_enricher.ts | 42 +++++++++++++++++++ .../shared/index_management/server/types.ts | 2 + 5 files changed, 71 insertions(+), 37 deletions(-) delete mode 100644 x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts create mode 100644 x-pack/platform/plugins/shared/index_management/server/services/index.ts create mode 100644 x-pack/platform/plugins/shared/index_management/server/services/index_data_enricher.ts diff --git a/x-pack/platform/plugins/shared/index_management/server/plugin.ts b/x-pack/platform/plugins/shared/index_management/server/plugin.ts index 529d18723d6bc..638eda8e8f9de 100644 --- a/x-pack/platform/plugins/shared/index_management/server/plugin.ts +++ b/x-pack/platform/plugins/shared/index_management/server/plugin.ts @@ -10,19 +10,28 @@ import type { CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/serv import { PLUGIN } from '../common/constants/plugin'; import type { Dependencies } from './types'; import { ApiRoutes } from './routes'; +import { IndexDataEnricher } from './services'; import { handleEsError } from './shared_imports'; import type { IndexManagementConfig } from './config'; -export class IndexMgmtServerPlugin implements Plugin { +export interface IndexManagementPluginSetup { + indexDataEnricher: { + add: IndexDataEnricher['add']; + }; +} + +export class IndexMgmtServerPlugin implements Plugin { private readonly apiRoutes: ApiRoutes; + private readonly indexDataEnricher: IndexDataEnricher; private readonly config: IndexManagementConfig; constructor(initContext: PluginInitializerContext) { this.apiRoutes = new ApiRoutes(); + this.indexDataEnricher = new IndexDataEnricher(); this.config = initContext.config.get(); } - setup({ http }: CoreSetup, { features, security }: Dependencies) { + setup({ http }: CoreSetup, { features, security }: Dependencies): IndexManagementPluginSetup { features.registerElasticsearchFeature({ id: PLUGIN.id, privileges: [ @@ -61,10 +70,17 @@ export class IndexMgmtServerPlugin implements Plugin { enableFailureStoreRetentionDisabling: this.config.enableFailureStoreRetentionDisabling ?? true, }, + indexDataEnricher: this.indexDataEnricher, lib: { handleEsError, }, }); + + return { + indexDataEnricher: { + add: this.indexDataEnricher.add.bind(this.indexDataEnricher), + }, + }; } start() {} diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts deleted file mode 100644 index 44ccb62d16ad1..0000000000000 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/metering_stats.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type { RouteDependencies } from '../../../types'; -import { addBasePath } from '..'; - -export function registerMeteringStats({ router, lib: { handleEsError } }: RouteDependencies) { - router.get( - { - path: addBasePath('/metering_stats'), - security: { - authz: { - enabled: false, - reason: 'Relies on es client for authorization', - }, - }, - validate: false, - }, - async (context, request, response) => { - const { client } = (await context.core).elasticsearch; - try { - const data = await client.asSecondaryAuthUser.transport.request({ - method: 'GET', - path: `/_metering/stats/` + '*', // indexNamesString, - }); - return response.ok({ body: data }); - } catch (error) { - return handleEsError({ error, response }); - } - } - ); -} diff --git a/x-pack/platform/plugins/shared/index_management/server/services/index.ts b/x-pack/platform/plugins/shared/index_management/server/services/index.ts new file mode 100644 index 0000000000000..bd62f2df80cc8 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/server/services/index.ts @@ -0,0 +1,9 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { Enricher } from './index_data_enricher'; +export { IndexDataEnricher } from './index_data_enricher'; diff --git a/x-pack/platform/plugins/shared/index_management/server/services/index_data_enricher.ts b/x-pack/platform/plugins/shared/index_management/server/services/index_data_enricher.ts new file mode 100644 index 0000000000000..7138b0ccc53f9 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/server/services/index_data_enricher.ts @@ -0,0 +1,42 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { IScopedClusterClient } from '@kbn/core/server'; +import type { Index } from '..'; + +export type Enricher = (indices: Index[], client: IScopedClusterClient) => Promise; + +export class IndexDataEnricher { + private readonly _enrichers: Enricher[] = []; + + public add(enricher: Enricher) { + this._enrichers.push(enricher); + } + + public enrichIndices = async ( + indices: Index[], + client: IScopedClusterClient + ): Promise => { + let enrichedIndices = indices; + + for (let i = 0; i < this.enrichers.length; i++) { + const dataEnricher = this.enrichers[i]; + try { + const dataEnricherResponse = await dataEnricher(enrichedIndices, client); + enrichedIndices = dataEnricherResponse; + } catch (e) { + // silently swallow enricher response errors + } + } + + return enrichedIndices; + }; + + public get enrichers() { + return this._enrichers; + } +} diff --git a/x-pack/platform/plugins/shared/index_management/server/types.ts b/x-pack/platform/plugins/shared/index_management/server/types.ts index 80ac1aefb4488..227a219f70ce5 100644 --- a/x-pack/platform/plugins/shared/index_management/server/types.ts +++ b/x-pack/platform/plugins/shared/index_management/server/types.ts @@ -10,6 +10,7 @@ import type { IRouter } from '@kbn/core/server'; import type { FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; +import type { IndexDataEnricher } from './services'; import type { handleEsError } from './shared_imports'; export interface Dependencies { @@ -31,6 +32,7 @@ export interface RouteDependencies { enableProjectLevelRetentionChecks: boolean; enableFailureStoreRetentionDisabling: boolean; }; + indexDataEnricher: IndexDataEnricher; lib: { handleEsError: typeof handleEsError; }; From d2edb8c1098eac63b5fe374cd223f08915c7d40e Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 14 Jan 2026 01:16:23 -0600 Subject: [PATCH 05/55] partial progress --- .../public/ccr_data_enricher.ts | 5 +- .../index_management/common/types/indices.ts | 17 +++ .../public/application/services/api.ts | 115 +++--------------- .../index_management/server/lib/types.ts | 5 + .../server/routes/api/indices/indices_get.ts | 68 ++++++++++- .../api/indices/register_indices_routes.ts | 2 - 6 files changed, 106 insertions(+), 106 deletions(-) diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts index 478fea7006c02..2a558a5184089 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts @@ -1,4 +1,4 @@ -=/* +/* * 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; you may not use this file except in compliance with the Elastic License @@ -8,7 +8,8 @@ import type { HttpSetup } from '@kbn/core/public'; import type { CcrFollowInfoResponse } from '@elastic/elasticsearch/lib/api/types'; -export const ccrDataEnricher = async (client: HttpSetup) => client.get('/internal/ccr/follower_info'); +export const ccrDataEnricher = async (client: HttpSetup) => + client.get('/internal/ccr/follower_info'); /* this is just setting a boolean whether its a follower index diff --git a/x-pack/platform/plugins/shared/index_management/common/types/indices.ts b/x-pack/platform/plugins/shared/index_management/common/types/indices.ts index eeda7aafdd0d8..3254057c6c248 100644 --- a/x-pack/platform/plugins/shared/index_management/common/types/indices.ts +++ b/x-pack/platform/plugins/shared/index_management/common/types/indices.ts @@ -31,3 +31,20 @@ export interface IndexSettingsResponse { settings: IndexSettings; defaults: IndexSettings; } + +export interface IndexData { + documents?: number; + size?: string; + name: string; + isFrozen: boolean; + aliases?: string[]; + hidden: boolean; + data_stream?: string; + mode?: string; + primary?: number; + replica?: number; +} + +export interface IndexDataResponse { + body: IndexData[]; +} diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 200f694da02f7..881da9e37b48a 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -8,10 +8,7 @@ import { ByteSizeValue } from '@kbn/config-schema'; import { METRIC_TYPE } from '@kbn/analytics'; import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; -import type { - IndicesStatsResponse, - IndicesGetResponse, -} from '@elastic/elasticsearch/lib/api/types'; +import type { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; import type { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; import type { ReindexService } from '@kbn/reindex-service-plugin/public'; @@ -52,15 +49,12 @@ import { httpService } from './http'; import type { UiMetricService } from './ui_metric'; import type { FieldFromIndicesRequest } from '../../../common'; import type { Fields } from '../components/mappings_editor/types'; +import type { IndexData } from '../../../common/types/indices'; interface ReloadIndicesOptions { asSystemRequest?: boolean; } -interface MeteringStatsResponse { - indices: MeteringStats[]; -} - // Temporary hack to provide the uiMetricService and reindexService instance to this file. // TODO: Refactor and export an ApiService instance through the app dependencies context let uiMetricService: UiMetricService; @@ -152,10 +146,10 @@ interface IndexMetadata { primary?: Number; replica?: Number; isFrozen: boolean; - aliases: string[] | 'none'; + aliases?: string[]; hidden: boolean; - data_stream: string; - mode: string; + data_stream?: string; + mode?: string; health?: string; status?: string; uuid?: string; @@ -165,25 +159,6 @@ interface IndexMetadata { primary_size?: string; } -const getBaseResponse = ({ - indexName, - indexData, - aliases, -}: { - indexName: string; - indexData: any; - aliases: string[]; -}): IndexMetadata => ({ - name: indexName, - primary: indexData.settings?.index?.number_of_shards as Number, - replica: indexData.settings?.index?.number_of_replicas as Number, - isFrozen: indexData.settings?.index?.frozen === 'true', - aliases: aliases.length ? aliases : 'none', - hidden: indexData.settings?.index?.hidden === 'true', - data_stream: indexData.data_stream as string, - mode: indexData.settings?.index?.mode as string, -}); - export async function loadIndices() { const config = { isIndexStatsEnabled: true, @@ -192,76 +167,32 @@ export async function loadIndices() { // isSizeAndDocCountEnabled: false, }; - const promises: [ - Promise, - Promise, - Promise - ][] = []; + const promises: [Promise>, Promise][] = + []; - const getIndicesPromise = httpService.httpClient.get( + const getIndicesPromise = httpService.httpClient.get>( `${API_BASE_PATH}/indices_get` ); // @ts-expect-error promises.push([getIndicesPromise]); if (config.isIndexStatsEnabled) { - const getIndicesStatsPromise = httpService.httpClient.get( + const getIndicesStatsPromise = httpService.httpClient.get( `${API_BASE_PATH}/indices_stats` ); promises[0].push(getIndicesStatsPromise); } else { - promises[0].push(Promise.resolve({})); - } - - // uses the _metering/stats API to get the number of documents and size of the index - // this API is only available in ES3 - if (config.isSizeAndDocCountEnabled) { - // todo - const meteringStatsPromise = httpService.httpClient - .get(`${API_BASE_PATH}/metering_stats`) - .catch(() => ({} as MeteringStatsResponse)); - - // todo why does this depend upon elevated privs? - /* - const getMeteringStatsPromise = - client.asSecondaryAuthUser.transport.request({ - method: 'GET', - path: `/_metering/stats/` + indexNamesString, - }); - */ - promises[0].push(meteringStatsPromise); - } else { - promises[0].push(Promise.resolve({})); + promises[0].push(Promise.resolve({} as IndicesStatsResponse['indices'])); } // todo - this will run at speed of slowest promise // would be better to run independently and combine later - const [indices, indicesStats, meteringStats] = await Promise.all(promises[0]); + const [indices, indicesStats] = await Promise.all(promises[0]); - const indicesNames = Object.keys(indices); + const indicesWithMetadata: Record = indices; - if (!indicesNames.length) { - return []; - } - - // todo better type - const mainBaseResponse = indicesNames.reduce>( - (collector, indexName: string) => { - const indexData = indices[indexName]; - const aliases = Object.keys(indexData.aliases!); - collector[indexName] = getBaseResponse({ - indexName, - indexData, - aliases, - }); - - return collector; - }, - {} - ); - - if (indicesStats?.indices) { - Object.values(mainBaseResponse).forEach((baseResponse) => { - const indexStats = indicesStats.indices![baseResponse.name] || {}; + if (Object.keys(indicesStats || {}).length > 0) { + Object.values(indicesWithMetadata).forEach((baseResponse) => { + const indexStats = indicesStats![baseResponse.name] || {}; baseResponse.health = indexStats?.health; baseResponse.status = indexStats?.status; @@ -277,25 +208,13 @@ export async function loadIndices() { }); } - const meteringStatsWithTypes = meteringStats as MeteringStatsResponse | undefined; - - if (meteringStatsWithTypes?.indices?.length === 0) { - meteringStatsWithTypes?.indices?.forEach((indexStat) => { - const baseResponse = mainBaseResponse[indexStat.name]; - if (baseResponse) { - baseResponse.documents = indexStat.num_docs; - baseResponse.size = new ByteSizeValue(indexStat.size_in_bytes).toString(); - } - }); - } - // todo change api - const enriched = await indexDataEnricher.enrichIndices(indices, httpService.httpClient); + // const enriched = await indexDataEnricher.enrichIndices(indices, httpService.httpClient); // if neither index stats (Stateful only API) // nor size and doc count are enabled (ES3 only API) // return the base response - return Object.values(mainBaseResponse); + return Object.values(indicesWithMetadata); } export async function reloadIndices( diff --git a/x-pack/platform/plugins/shared/index_management/server/lib/types.ts b/x-pack/platform/plugins/shared/index_management/server/lib/types.ts index 1657c4bbbb6e0..f3ba49076f6b2 100644 --- a/x-pack/platform/plugins/shared/index_management/server/lib/types.ts +++ b/x-pack/platform/plugins/shared/index_management/server/lib/types.ts @@ -10,3 +10,8 @@ export interface MeteringStats { num_docs: number; size_in_bytes: number; } + +// is this used? +export interface MeteringStatsResponse { + indices: MeteringStats[]; +} diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts index 761defa9f4880..99745fbb59291 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts @@ -4,10 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { IndicesGetResponse } from '@elastic/elasticsearch/lib/api/types'; +import { ByteSizeValue } from '@kbn/config-schema'; import type { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import type { MeteringStatsResponse } from '../../../lib/types'; +import type { IndexData } from '../../../../common/types/indices'; -export function registerIndicesGet({ router, lib: { handleEsError } }: RouteDependencies) { +export function registerIndicesGet({ router, lib: { handleEsError }, config }: RouteDependencies) { router.get( { path: addBasePath('/indices_get'), @@ -21,9 +25,11 @@ export function registerIndicesGet({ router, lib: { handleEsError } }: RouteDepe }, async (context, request, response) => { const { client } = (await context.core).elasticsearch; + const promiseArray: [Promise, Promise][] = []; + try { - const indices = await client.asCurrentUser.indices.get({ - index: '*', // indexNamesString, + const indicesPromise = client.asCurrentUser.indices.get({ + index: '*', expand_wildcards: ['hidden', 'all'], // only get specified index properties from ES to keep the response under 536MB // node.js string length limit: https://github.com/nodejs/node/issues/33960 @@ -39,7 +45,61 @@ export function registerIndicesGet({ router, lib: { handleEsError } }: RouteDepe // for better performance only compute aliases and settings of indices but not mappings features: ['aliases', 'settings'], }); - return response.ok({ body: indices }); + + // @ts-ignore + promiseArray[0] = [indicesPromise]; + + // todo when is this used? + if (config.isSizeAndDocCountEnabled) { + const statsPromise = client.asSecondaryAuthUser.transport + .request({ + method: 'GET', + path: '/_metering/stats/*', + }) + .catch(() => ({ indices: [] })); + promiseArray[0].push(statsPromise); + } else { + promiseArray[0].push(Promise.resolve({ indices: [] })); + } + + const [indices, { indices: indicesStats }] = await Promise.all(promiseArray[0]); + + const indexStatsMap = indicesStats.reduce((prev, index) => { + prev[index.name] = { size_in_bytes: index.size_in_bytes, num_docs: index.num_docs }; + return prev; + }, {} as Record); + + const mappedIndices: Record = Object.keys(indices).reduce( + (prev, indexName: string) => { + // todo this code is duplicated and should be shared + const indexData = indices[indexName]; + const aliases = Object.keys(indexData.aliases!); + prev[indexName] = { + name: indexName, + primary: Number(indexData.settings?.index?.number_of_shards), + replica: Number(indexData.settings?.index?.number_of_replicas), + isFrozen: indexData.settings?.index?.frozen === 'true', + hidden: indexData.settings?.index?.hidden === 'true', + data_stream: indexData.data_stream, + mode: indexData.settings?.index?.mode, + }; + + if (aliases.length) { + prev[indexName].aliases = aliases; + } + + if (indexStatsMap[indexName]) { + prev[indexName].documents = indexStatsMap[indexName].num_docs ?? 0; + prev[indexName].size = new ByteSizeValue( + indexStatsMap[indexName].size_in_bytes ?? 0 + ).toString(); + } + return prev; + }, + {} as Record + ); + + return response.ok({ body: mappedIndices }); } catch (error) { return handleEsError({ error, response }); } diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts index 4e7cd6332a037..de251803a979e 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_indices_routes.ts @@ -21,7 +21,6 @@ import { registerCreateRoute } from './register_create_route'; import { registerIndicesGet } from './indices_get'; import { registerIndicesStats } from './indices_stats'; -import { registerMeteringStats } from './metering_stats'; export function registerIndicesRoutes(dependencies: RouteDependencies) { registerClearCacheRoute(dependencies); @@ -38,5 +37,4 @@ export function registerIndicesRoutes(dependencies: RouteDependencies) { registerIndicesGet(dependencies); registerIndicesStats(dependencies); - registerMeteringStats(dependencies); } From b17a31a2a76f79667f93fa6b1c61f16fee3bbf3b Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 14 Jan 2026 08:24:05 -0600 Subject: [PATCH 06/55] revert unneeded changes --- .../server/plugin.ts | 53 ++++++++++++++++++- .../cross_cluster_replication/server/types.ts | 4 ++ .../server/plugin.ts | 29 +++++++++- .../server/types.ts | 2 + .../plugins/private/rollup/server/plugin.ts | 7 ++- .../rollup/server/rollup_data_enricher.ts | 36 +++++++++++++ .../plugins/private/rollup/server/types.ts | 3 ++ .../index_list/index_table/index_table.js | 2 - .../application/store/actions/load_indices.js | 1 - 9 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 x-pack/platform/plugins/private/rollup/server/rollup_data_enricher.ts diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts index d9c21dcb3f729..58099417c349f 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/plugin.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { Observable } from 'rxjs'; +import { firstValueFrom } from 'rxjs'; import type { CoreSetup, CoreStart, @@ -12,22 +14,71 @@ import type { Logger, PluginInitializerContext, } from '@kbn/core/server'; +import type { IScopedClusterClient } from '@kbn/core/server'; +import type { Index } from '@kbn/index-management-plugin/server'; import { PLUGIN } from '../common/constants'; import type { SetupDependencies, StartDependencies } from './types'; import { registerApiRoutes } from './routes'; +import type { CrossClusterReplicationConfig } from './config'; import { License, handleEsError } from './shared_imports'; +const ccrDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => { + if (!indicesList?.length) { + return indicesList; + } + + try { + const { follower_indices: followerIndices } = await client.asCurrentUser.ccr.followInfo({ + index: '_all', + }); + + return indicesList.map((index) => { + const isFollowerIndex = !!followerIndices.find( + (followerIndex: { follower_index: string }) => { + return followerIndex.follower_index === index.name; + } + ); + return { + ...index, + isFollowerIndex, + }; + }); + } catch (e) { + return indicesList; + } +}; + export class CrossClusterReplicationServerPlugin implements Plugin { + private readonly config$: Observable; private readonly license: License; private readonly logger: Logger; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); + this.config$ = initializerContext.config.create(); this.license = new License(); } - setup({ http }: CoreSetup, { features }: SetupDependencies) { + setup( + { http, getStartServices }: CoreSetup, + { features, licensing, indexManagement, remoteClusters }: SetupDependencies + ) { + void firstValueFrom(this.config$).then((config) => { + // remoteClusters.isUiEnabled is driven by the xpack.remote_clusters.ui.enabled setting. + // The CCR UI depends upon the Remote Clusters UI (e.g. by cross-linking to it), so if + // the Remote Clusters UI is disabled we can't show the CCR UI. + const isCcrUiEnabled = config.ui.enabled && remoteClusters.isUiEnabled; + + // If the UI isn't enabled, then we don't want to expose any CCR concepts in the UI, including + // "follower" badges for follower indices. + if (isCcrUiEnabled) { + if (indexManagement.indexDataEnricher) { + indexManagement.indexDataEnricher.add(ccrDataEnricher); + } + } + }); + this.license.setup({ pluginName: PLUGIN.TITLE, logger: this.logger, diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts index e24a73f523baf..a85dc733c30ce 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/types.ts @@ -8,10 +8,14 @@ import type { IRouter } from '@kbn/core/server'; import type { FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/server'; +import type { IndexManagementPluginSetup } from '@kbn/index-management-plugin/server'; +import type { RemoteClustersPluginSetup } from '@kbn/remote-clusters-plugin/server'; import type { License, handleEsError } from './shared_imports'; export interface SetupDependencies { licensing: LicensingPluginSetup; + indexManagement: IndexManagementPluginSetup; + remoteClusters: RemoteClustersPluginSetup; features: FeaturesPluginSetup; } diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts b/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts index 2926b4ae9f7d5..1c54971eebed8 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/server/plugin.ts @@ -7,7 +7,9 @@ import { i18n } from '@kbn/i18n'; import type { CoreSetup, Plugin, Logger, PluginInitializerContext } from '@kbn/core/server'; +import type { IScopedClusterClient } from '@kbn/core/server'; +import type { Index } from '@kbn/index-management-plugin/common/types'; import { PLUGIN } from '../common/constants'; import type { Dependencies } from './types'; import { registerApiRoutes } from './routes'; @@ -15,6 +17,25 @@ import { License } from './services'; import type { IndexLifecycleManagementConfig } from './config'; import { handleEsError } from './shared_imports'; +const indexLifecycleDataEnricher = async ( + indicesList: Index[], + client: IScopedClusterClient +): Promise => { + if (!indicesList || !indicesList.length) { + return []; + } + + const { indices: ilmIndicesData } = await client.asCurrentUser.ilm.explainLifecycle({ + index: '*,.*', + }); + return indicesList.map((index: Index) => { + return { + ...index, + ilm: { ...(ilmIndicesData[index.name] || {}) }, + }; + }); +}; + export class IndexLifecycleManagementServerPlugin implements Plugin { private readonly config: IndexLifecycleManagementConfig; private readonly license: License; @@ -26,7 +47,7 @@ export class IndexLifecycleManagementServerPlugin implements Plugin { @@ -30,7 +31,7 @@ export class RollupPlugin implements Plugin { public setup( { http, uiSettings, getStartServices }: CoreSetup, - { features, licensing, usageCollection, dataViews, data }: Dependencies + { features, licensing, indexManagement, usageCollection, dataViews, data }: Dependencies ) { this.license.setup( { @@ -101,6 +102,10 @@ export class RollupPlugin implements Plugin { } if (this.config.ui.enabled) { + if (indexManagement && indexManagement.indexDataEnricher) { + indexManagement.indexDataEnricher.add(rollupDataEnricher); + } + dataViews.enableRollups(); data.search.enableRollups(); } diff --git a/x-pack/platform/plugins/private/rollup/server/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/server/rollup_data_enricher.ts new file mode 100644 index 0000000000000..1934096de453e --- /dev/null +++ b/x-pack/platform/plugins/private/rollup/server/rollup_data_enricher.ts @@ -0,0 +1,36 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { IScopedClusterClient } from '@kbn/core/server'; +import type { Index } from '@kbn/index-management-plugin/server'; +import { isArray } from 'lodash'; + +export const rollupDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => { + if (!indicesList || !indicesList.length) { + return Promise.resolve(indicesList); + } + + try { + const rollupJobData = await client.asCurrentUser.rollup.getRollupIndexCaps({ + index: '_all', + }); + + return indicesList.map((index) => { + let isRollupIndex = !!rollupJobData[index.name]; + if (!isRollupIndex && isArray(index.aliases)) { + isRollupIndex = index.aliases.some((alias) => !!rollupJobData[alias]); + } + return { + ...index, + isRollupIndex, + }; + }); + } catch (e) { + // swallow exceptions and return original list + return indicesList; + } +}; diff --git a/x-pack/platform/plugins/private/rollup/server/types.ts b/x-pack/platform/plugins/private/rollup/server/types.ts index 630b421bbbee6..572d16899ead0 100644 --- a/x-pack/platform/plugins/private/rollup/server/types.ts +++ b/x-pack/platform/plugins/private/rollup/server/types.ts @@ -7,6 +7,8 @@ import type { IRouter } from '@kbn/core/server'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; + +import type { IndexManagementPluginSetup } from '@kbn/index-management-plugin/server'; import type { FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { DataViewsServerPluginSetup } from '@kbn/data-views-plugin/server'; import type { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; @@ -15,6 +17,7 @@ import type { handleEsError } from '@kbn/es-ui-shared-plugin/server'; import type { License } from './services'; export interface Dependencies { + indexManagement?: IndexManagementPluginSetup; usageCollection?: UsageCollectionSetup; licensing: LicensingPluginSetup; features: FeaturesPluginSetup; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index 17d585f154370..37c257995f63c 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -542,8 +542,6 @@ export class IndexTable extends Component { ); } - console.log('indicesError', indicesError); - if (indicesError) { if (indicesError.status === 403) { return ( diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js index 729c90a8e2010..f5a5679b02767 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js @@ -18,7 +18,6 @@ export const loadIndices = () => async (dispatch) => { try { indices = await request(); } catch (error) { - console.log('loadIndices error', error); return dispatch(loadIndicesError(error)); } dispatch(loadIndicesSuccess({ indices })); From 151949f030de08eaa49f2e600058e544699340e3 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 14 Jan 2026 22:59:31 -0600 Subject: [PATCH 07/55] simplify browser side index data enhancement api --- .../src/types.ts | 2 +- .../public/application/services/api.ts | 1 + .../shared/index_management/public/plugin.ts | 4 ++-- .../index_management/public/services/index.ts | 2 +- .../public/services/index_data_enricher.ts | 19 ++++--------------- .../index_management/server/lib/types.ts | 1 - 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index 7e4d8bffee266..8aeb0ffe8309a 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -19,7 +19,7 @@ import type { HttpSetup } from '@kbn/core/public'; import type { ExtensionsSetup } from './services/extensions_service'; import type { PublicApiServiceSetup } from './services/public_api_service'; -export type Enricher = (indices: Index[], client: HttpSetup) => Promise; +export type Enricher = (client: HttpSetup) => Promise; export type IndexManagementLocatorParams = SerializableRecord & ( diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index baa7037ed3f05..c5dc9c3bfed04 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -161,6 +161,7 @@ interface IndexMetadata { } export async function loadIndices() { + // todo get config const config = { isIndexStatsEnabled: true, isSizeAndDocCountEnabled: true, diff --git a/x-pack/platform/plugins/shared/index_management/public/plugin.ts b/x-pack/platform/plugins/shared/index_management/public/plugin.ts index 41c46e381d856..c647c429e831b 100644 --- a/x-pack/platform/plugins/shared/index_management/public/plugin.ts +++ b/x-pack/platform/plugins/shared/index_management/public/plugin.ts @@ -46,7 +46,7 @@ import { IndexManagementLocatorDefinition } from './locator'; import { ComponentTemplateFlyout } from './application/components/component_templates/component_templates_flyout_embeddable'; import { DataStreamFlyout } from './application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_flyout_embeddable'; import { IndexTemplateFlyout } from './application/sections/home/template_list/template_details/index_template_flyout_embeddable'; -import { IndexDataEnricher } from './services'; +import { indexDataEnricher, type IndexDataEnricher } from './services'; export class IndexMgmtUIPlugin implements @@ -118,7 +118,7 @@ export class IndexMgmtUIPlugin enableFailureStoreRetentionDisabling: enableFailureStoreRetentionDisabling ?? true, }; - this.indexDataEnricher = new IndexDataEnricher(); + this.indexDataEnricher = indexDataEnricher; } public setup( diff --git a/x-pack/platform/plugins/shared/index_management/public/services/index.ts b/x-pack/platform/plugins/shared/index_management/public/services/index.ts index e26aab7f58ca0..03833cdd3357a 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/index.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index.ts @@ -9,5 +9,5 @@ export { ExtensionsService } from './extensions_service'; export { PublicApiService } from './public_api_service'; -export { IndexDataEnricher } from './index_data_enricher'; +export { IndexDataEnricher, indexDataEnricher } from './index_data_enricher'; export type { Enricher } from '@kbn/index-management-shared-types'; diff --git a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts index 234a5e87a6c4c..c4434d10d8924 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts @@ -16,24 +16,13 @@ export class IndexDataEnricher { this._enrichers.push(enricher); } - public enrichIndices = async (indices: Index[], client: HttpSetup): Promise => { - let enrichedIndices = indices; - - for (let i = 0; i < this.enrichers.length; i++) { - const dataEnricher = this.enrichers[i]; - try { - // todo do this in parallel - const dataEnricherResponse = await dataEnricher(enrichedIndices, client); - enrichedIndices = dataEnricherResponse; - } catch (e) { - // silently swallow enricher response errors - } - } - - return enrichedIndices; + public enrichIndices = (client: HttpSetup): Promise[] => { + return this.enrichers.map((enricher) => enricher(client)); }; public get enrichers() { return this._enrichers; } } + +export const indexDataEnricher = new IndexDataEnricher(); diff --git a/x-pack/platform/plugins/shared/index_management/server/lib/types.ts b/x-pack/platform/plugins/shared/index_management/server/lib/types.ts index 76e9cd9a280a7..902b5bf1669c6 100644 --- a/x-pack/platform/plugins/shared/index_management/server/lib/types.ts +++ b/x-pack/platform/plugins/shared/index_management/server/lib/types.ts @@ -11,7 +11,6 @@ export interface MeteringStats { size_in_bytes: number; } -// is this used? export interface MeteringStatsResponse { indices: MeteringStats[]; } From b1b8151ac5de627a2cb7735c7e18821adb75f8b4 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sat, 17 Jan 2026 16:14:06 -0600 Subject: [PATCH 08/55] loading data via multiple requests and merging --- .../src/types.ts | 7 +- .../public/ccr_data_enricher.ts | 31 +++---- .../register_get_follower_info.ts | 8 +- .../public/index_lifecycle_data_enricher.ts | 41 +++++----- .../server/routes/api/policies/index.ts | 2 + .../api/policies/register_explain_route.ts | 37 +++++++++ .../rollup/public/rollup_data_enricher.ts | 39 +++++++-- .../public/application/services/api.ts | 82 ++++++++----------- .../public/index_stats_enricher.ts | 43 ++++++++++ .../shared/index_management/public/plugin.ts | 6 ++ .../public/services/index_data_enricher.ts | 6 +- .../server/routes/api/indices/indices_get.ts | 19 ++--- 12 files changed, 214 insertions(+), 107 deletions(-) create mode 100644 x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts create mode 100644 x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index 8aeb0ffe8309a..8e310f7087cce 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -19,7 +19,12 @@ import type { HttpSetup } from '@kbn/core/public'; import type { ExtensionsSetup } from './services/extensions_service'; import type { PublicApiServiceSetup } from './services/public_api_service'; -export type Enricher = (client: HttpSetup) => Promise; +export interface EnricherResponse { + source: string; + indices?: Partial[]; + error?: string; +} +export type Enricher = (client: HttpSetup) => Promise; export type IndexManagementLocatorParams = SerializableRecord & ( diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts index 2a558a5184089..cf6182ac958ef 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts @@ -7,21 +7,24 @@ import type { HttpSetup } from '@kbn/core/public'; import type { CcrFollowInfoResponse } from '@elastic/elasticsearch/lib/api/types'; +import type { EnricherResponse } from '@kbn/index-management-shared-types'; +const SOURCE = 'ccr_data_enricher'; -export const ccrDataEnricher = async (client: HttpSetup) => - client.get('/internal/ccr/follower_info'); - -/* -this is just setting a boolean whether its a follower index -indicesList.map((index) => { - const isFollowerIndex = !!followerIndices.find( - (followerIndex: { follower_index: string }) => { - return followerIndex.follower_index === index.name; - } - ); +export const ccrDataEnricher = async (client: HttpSetup): Promise => + client + .get('/api/cross_cluster_replication/follower_info') + .then((response) => { + return { + indices: response.follower_indices.map((followerIndex) => ({ + name: followerIndex.follower_index, + isFollowerIndex: true, + })), + source: SOURCE, + }; + }) + .catch((error) => { return { - ...index, - isFollowerIndex, + error: 'Failed to load cross cluster replication data', + source: SOURCE, }; }); - */ diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts index 2ab01145c7b3f..b34ed314e138c 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts @@ -5,13 +5,9 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; import { addBasePath } from '../../../services'; import type { RouteDependencies } from '../../../types'; -/** - * Returns a single follower index pattern - */ export const registerGetFollowerInfoRoute = ({ router, license, @@ -26,9 +22,7 @@ export const registerGetFollowerInfoRoute = ({ reason: 'Relies on es client for authorization', }, }, - validate: { - params: schema.never(), - }, + validate: {}, }, license.guardApiRoute(async (context, request, response) => { const { client } = (await context.core).elasticsearch; diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts index 6e02931560838..a3bec696b8c10 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts @@ -5,24 +5,27 @@ * 2.0. */ -import type { Index } from '@kbn/index-management-shared-types'; +import type { IlmExplainLifecycleResponse } from '@elastic/elasticsearch/lib/api/types'; +import type { HttpSetup } from '@kbn/core/public'; +import type { EnricherResponse } from '@kbn/index-management-shared-types'; -export const indexLifecycleDataEnricher = async ( - indicesList: Index[], - client: IScopedClusterClient -): Promise => { - if (!indicesList || !indicesList.length) { - return []; - } +const SOURCE = 'index_lifecycle_data_enricher'; - const { indices: ilmIndicesData } = await client.asCurrentUser.ilm.explainLifecycle({ - index: '*,.*', - }); - return indicesList.map((index: Index) => { - return { - ...index, - // simply appends ilm data if it exists - ilm: { ...(ilmIndicesData[index.name] || {}) }, - }; - }); -}; +export const indexLifecycleDataEnricher = async (client: HttpSetup): Promise => + client + .get('/api/index_lifecycle_management/explain') + .then((response) => { + return { + indices: Object.keys(response.indices).map((index) => ({ + name: index, + ilm: response.indices[index], + })), + source: SOURCE, + }; + }) + .catch((error) => { + return { + error: 'Failed to load index lifecycle data', + source: SOURCE, + }; + }); diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/index.ts b/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/index.ts index e5d04609e458b..97084b0cc287f 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/index.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/index.ts @@ -9,9 +9,11 @@ import type { RouteDependencies } from '../../../types'; import { registerFetchRoute } from './register_fetch_route'; import { registerCreateRoute } from './register_create_route'; import { registerDeleteRoute } from './register_delete_route'; +import { registerExplainRoute } from './register_explain_route'; export function registerPoliciesRoutes(dependencies: RouteDependencies) { registerFetchRoute(dependencies); registerCreateRoute(dependencies); registerDeleteRoute(dependencies); + registerExplainRoute(dependencies); } diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts b/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts new file mode 100644 index 0000000000000..8bf9168447cae --- /dev/null +++ b/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts @@ -0,0 +1,37 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { addBasePath } from '../../../services'; +import type { RouteDependencies } from '../../../types'; + +export const registerExplainRoute = ({ + router, + license, + lib: { handleEsError }, +}: RouteDependencies) => { + router.get( + { + path: addBasePath('/explain'), + security: { + authz: { + enabled: false, + reason: 'Relies on es client for authorization', + }, + }, + validate: {}, + }, + license.guardApiRoute(async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + try { + const body = await client.asCurrentUser.ilm.explainLifecycle({ index: '*,.*' }); + return response.ok({ body }); + } catch (error) { + return handleEsError({ error, response }); + } + }) + ); +}; diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index 584389ff19b25..179ae6d90f9ef 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -6,14 +6,39 @@ */ import type { HttpSetup } from '@kbn/core/public'; -import type { Index } from '@kbn/index-management-shared-types'; -import { isArray } from 'lodash'; +import type { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api/types'; +import type { EnricherResponse } from '@kbn/index-management-shared-types'; -export const rollupDataEnricher = async (indicesList: Index[], client: HttpSetup) => { - if (!indicesList || !indicesList.length) { - return Promise.resolve(indicesList); - } +const SOURCE = 'rollup_data_enricher'; + +// todo +// look at x-pack/platform/plugins/private/rollup/server/rollup_data_enricher.ts +// account for aliases +export const rollupDataEnricher = async (client: HttpSetup): Promise => + client + .get('/api/rollup/indices') + .then((response) => { + return { + indices: Object.keys(response).reduce((acc, rollupJob) => { + response[rollupJob].rollup_jobs.forEach((job) => { + acc.push({ + name: job.rollup_index, + isRollupIndex: true, + }); + }); + return acc; + }, [] as { name: string; isRollupIndex: true }[]), + source: SOURCE, + }; + }) + .catch((error) => { + return { + error: 'Failed to load rollup data', + source: SOURCE, + }; + }); +/* try { const rollupJobData = await client.asCurrentUser.rollup.getRollupIndexCaps({ index: '_all', @@ -33,4 +58,4 @@ export const rollupDataEnricher = async (indicesList: Index[], client: HttpSetup // swallow exceptions and return original list return indicesList; } -}; +*/ diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index c5dc9c3bfed04..dd7d873d8a80d 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -51,6 +51,7 @@ import type { FieldFromIndicesRequest } from '../../../common'; import type { Fields } from '../components/mappings_editor/types'; import type { IndexData } from '../../../common/types/indices'; import type { UserStartPrivilegesResponse } from '../../../server/lib/types'; +import { indexDataEnricher } from '../../services'; interface ReloadIndicesOptions { asSystemRequest?: boolean; @@ -161,61 +162,46 @@ interface IndexMetadata { } export async function loadIndices() { - // todo get config - const config = { - isIndexStatsEnabled: true, - isSizeAndDocCountEnabled: true, - // until endpoit is implemented - // isSizeAndDocCountEnabled: false, - }; - - const promises: [Promise>, Promise][] = - []; + // todo change api + // Run all requests in parallel + const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); - const getIndicesPromise = httpService.httpClient.get>( + // we'll wait for the main request to complete first + // the index list has stability - unsure if this is an actual + // issue but its certainly possible looking at the code + const indices = await httpService.httpClient.get>( `${API_BASE_PATH}/indices_get` ); - // @ts-expect-error - promises.push([getIndicesPromise]); - if (config.isIndexStatsEnabled) { - const getIndicesStatsPromise = httpService.httpClient.get( - `${API_BASE_PATH}/indices_stats` - ); - promises[0].push(getIndicesStatsPromise); - } else { - promises[0].push(Promise.resolve({} as IndicesStatsResponse['indices'])); - } - // todo - this will run at speed of slowest promise - // would be better to run independently and combine later - const [indices, indicesStats] = await Promise.all(promises[0]); + // todo emit update here + // todo review types const indicesWithMetadata: Record = indices; - if (Object.keys(indicesStats || {}).length > 0) { - Object.values(indicesWithMetadata).forEach((baseResponse) => { - const indexStats = indicesStats![baseResponse.name] || {}; - - baseResponse.health = indexStats?.health; - baseResponse.status = indexStats?.status; - baseResponse.uuid = indexStats?.uuid; - baseResponse.documents = indexStats?.primaries?.docs?.count ?? 0; - baseResponse.documents_deleted = indexStats?.primaries?.docs?.deleted ?? 0; - baseResponse.size = new ByteSizeValue( - indexStats?.total?.store?.size_in_bytes ?? 0 - ).toString(); - baseResponse.primary_size = new ByteSizeValue( - indexStats?.primaries?.store?.size_in_bytes ?? 0 - ).toString(); - }); - } - - // todo change api - // const enriched = await indexDataEnricher.enrichIndices(indices, httpService.httpClient); - - // if neither index stats (Stateful only API) - // nor size and doc count are enabled (ES3 only API) - // return the base response + // iterate over all the requests for additional info + enrichedPromises.forEach((enrichedPromise) => { + enrichedPromise + .then((enriched) => { + // todo + // iterate over the array of additional data and merge it into the original index data + if (enriched.indices) { + enriched.indices.forEach((enrichedIndex) => { + Object.assign(indicesWithMetadata[enrichedIndex.name], enrichedIndex); + }); + } else { + console.error(enriched.error); + } + }) + + .catch((error) => { + // silently swallow enricher response errors + // todo errors should be collected and displayed to the user + console.error(error); + }); + // todo add finally block to emit update here + }); + + // this is currently returning unenriched data return Object.values(indicesWithMetadata); } diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts new file mode 100644 index 0000000000000..b287cf7ed0bfc --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -0,0 +1,43 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { HttpSetup } from '@kbn/core/public'; +import type { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; +import { ByteSizeValue } from '@kbn/config-schema'; +import type { EnricherResponse } from '@kbn/index-management-shared-types'; +import { API_BASE_PATH } from '../common/constants'; + +const SOURCE = 'index_stats_enricher'; + +export const indexStatsEnricher = async (client: HttpSetup): Promise => + client + .get(`${API_BASE_PATH}/indices_stats`) + .then((response) => { + const indices = response.indices || {}; + + return { + indices: Object.keys(indices || {}).map((name) => ({ + name, + health: indices[name]?.health, + status: indices[name]?.status, + uuid: indices[name]?.uuid, + documents_deleted: indices[name]?.primaries?.docs?.deleted ?? 0, + primary_size: new ByteSizeValue( + indices[name]?.primaries?.store?.size_in_bytes ?? 0 + ).toString(), + documents: indices[name]?.primaries?.docs?.count ?? 0, + size: new ByteSizeValue(indices[name]?.total?.store?.size_in_bytes ?? 0).toString(), + })), + source: SOURCE, + }; + }) + .catch((error) => { + return { + error: 'Failed to load index stats', + source: SOURCE, + }; + }); diff --git a/x-pack/platform/plugins/shared/index_management/public/plugin.ts b/x-pack/platform/plugins/shared/index_management/public/plugin.ts index c647c429e831b..ee2737fa05fad 100644 --- a/x-pack/platform/plugins/shared/index_management/public/plugin.ts +++ b/x-pack/platform/plugins/shared/index_management/public/plugin.ts @@ -47,6 +47,7 @@ import { ComponentTemplateFlyout } from './application/components/component_temp import { DataStreamFlyout } from './application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_flyout_embeddable'; import { IndexTemplateFlyout } from './application/sections/home/template_list/template_details/index_template_flyout_embeddable'; import { indexDataEnricher, type IndexDataEnricher } from './services'; +import { indexStatsEnricher } from './index_stats_enricher'; export class IndexMgmtUIPlugin implements @@ -167,6 +168,11 @@ export class IndexMgmtUIPlugin this.apiService = new PublicApiService(coreSetup.http); + // disabled in serverless + if (this.config.enableIndexStats) { + this.indexDataEnricher.add(indexStatsEnricher); + } + return { apiService: this.apiService, extensionsService: this.extensionsService.setup(), diff --git a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts index c4434d10d8924..77336706ab277 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts @@ -16,7 +16,11 @@ export class IndexDataEnricher { this._enrichers.push(enricher); } - public enrichIndices = (client: HttpSetup): Promise[] => { + public enrichIndices = ( + client: HttpSetup + ): Promise<{ indices?: Index[]; error?: string; errorSource?: string }>[] => { + // this is complaining about the name field + // @ts-ignore return this.enrichers.map((enricher) => enricher(client)); }; diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts index 99745fbb59291..594cbda76d521 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { IndicesGetResponse } from '@elastic/elasticsearch/lib/api/types'; import { ByteSizeValue } from '@kbn/config-schema'; import type { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; @@ -25,7 +24,6 @@ export function registerIndicesGet({ router, lib: { handleEsError }, config }: R }, async (context, request, response) => { const { client } = (await context.core).elasticsearch; - const promiseArray: [Promise, Promise][] = []; try { const indicesPromise = client.asCurrentUser.indices.get({ @@ -46,23 +44,24 @@ export function registerIndicesGet({ router, lib: { handleEsError }, config }: R features: ['aliases', 'settings'], }); - // @ts-ignore - promiseArray[0] = [indicesPromise]; - - // todo when is this used? + // Used for serverless + let statsPromise: Promise | undefined; if (config.isSizeAndDocCountEnabled) { - const statsPromise = client.asSecondaryAuthUser.transport + // this api is internal only and therefore requires elevated privileges + statsPromise = client.asSecondaryAuthUser.transport .request({ method: 'GET', path: '/_metering/stats/*', }) .catch(() => ({ indices: [] })); - promiseArray[0].push(statsPromise); } else { - promiseArray[0].push(Promise.resolve({ indices: [] })); + statsPromise = Promise.resolve({ indices: [] }); } - const [indices, { indices: indicesStats }] = await Promise.all(promiseArray[0]); + const [indices, { indices: indicesStats }] = await Promise.all([ + indicesPromise, + statsPromise, + ]); const indexStatsMap = indicesStats.reduce((prev, index) => { prev[index.name] = { size_in_bytes: index.size_in_bytes, num_docs: index.num_docs }; From 728bc2c9f0ae8265292e03c029a2c84c77173dde Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sat, 17 Jan 2026 17:26:22 -0600 Subject: [PATCH 09/55] comment cleanup --- .../private/cross_cluster_replication/public/plugin.ts | 1 - x-pack/platform/plugins/private/rollup/public/plugin.ts | 1 - .../index_management/public/application/services/api.ts | 6 ++---- .../server/routes/api/indices/indices_get.ts | 1 - .../server/routes/api/indices/indices_stats.ts | 1 + 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts index 70f198b33d724..4a02fe0c12b3f 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts @@ -106,7 +106,6 @@ export class CrossClusterReplicationPlugin implements Plugin { indexManagement.extensionsService.addBadge(followerBadgeExtension); // note this isn't disabled if license changes - // todo need to fix indexManagement.indexDataEnricher.add(ccrDataEnricher); } } else { diff --git a/x-pack/platform/plugins/private/rollup/public/plugin.ts b/x-pack/platform/plugins/private/rollup/public/plugin.ts index a3bba5078b4fa..951426697cc8a 100644 --- a/x-pack/platform/plugins/private/rollup/public/plugin.ts +++ b/x-pack/platform/plugins/private/rollup/public/plugin.ts @@ -47,7 +47,6 @@ export class RollupPlugin implements Plugin { if (indexManagement) { indexManagement.extensionsService.addBadge(rollupBadgeExtension); indexManagement.extensionsService.addToggle(rollupToggleExtension); - // todo indexManagement.indexDataEnricher.add(rollupDataEnricher); } diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index dd7d873d8a80d..88112d34ad123 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -162,11 +162,10 @@ interface IndexMetadata { } export async function loadIndices() { - // todo change api // Run all requests in parallel const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); - // we'll wait for the main request to complete first + // we'll wait for the main request to complete first so // the index list has stability - unsure if this is an actual // issue but its certainly possible looking at the code const indices = await httpService.httpClient.get>( @@ -194,11 +193,10 @@ export async function loadIndices() { }) .catch((error) => { - // silently swallow enricher response errors // todo errors should be collected and displayed to the user console.error(error); }); - // todo add finally block to emit update here + // todo add finally block to emit update here? }); // this is currently returning unenriched data diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts index 594cbda76d521..d6eea4003638d 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts @@ -70,7 +70,6 @@ export function registerIndicesGet({ router, lib: { handleEsError }, config }: R const mappedIndices: Record = Object.keys(indices).reduce( (prev, indexName: string) => { - // todo this code is duplicated and should be shared const indexData = indices[indexName]; const aliases = Object.keys(indexData.aliases!); prev[indexName] = { diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts index b75dbd79f9967..008173193c44e 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts @@ -23,6 +23,7 @@ export function registerIndicesStats({ router, lib: { handleEsError } }: RouteDe const { client } = (await context.core).elasticsearch; try { const { indices } = await client.asCurrentUser.indices.stats({ + // todo does this need to be *,.* index: '*', // indexNamesString, expand_wildcards: ['hidden', 'all'], forbid_closed_indices: false, From e21c1a8167a531298a602374be726e170bdc3d00 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sun, 18 Jan 2026 00:18:05 -0600 Subject: [PATCH 10/55] largely working --- .../public/application/services/api.ts | 15 ++++++++------- .../application/store/actions/load_indices.js | 4 +--- .../public/index_stats_enricher.ts | 3 +-- .../server/routes/api/indices/indices_stats.ts | 5 +++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 88112d34ad123..27280f9a01b87 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -161,7 +161,7 @@ interface IndexMetadata { primary_size?: string; } -export async function loadIndices() { +export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { // Run all requests in parallel const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); @@ -172,35 +172,36 @@ export async function loadIndices() { `${API_BASE_PATH}/indices_get` ); - // todo emit update here + onIndicesLoaded(Object.values(indices)); // todo review types const indicesWithMetadata: Record = indices; + console.log('indicesWithMetadata', indicesWithMetadata); // iterate over all the requests for additional info enrichedPromises.forEach((enrichedPromise) => { enrichedPromise .then((enriched) => { - // todo // iterate over the array of additional data and merge it into the original index data if (enriched.indices) { enriched.indices.forEach((enrichedIndex) => { - Object.assign(indicesWithMetadata[enrichedIndex.name], enrichedIndex); + if (indicesWithMetadata[enrichedIndex.name]) { + Object.assign(indicesWithMetadata[enrichedIndex.name], enrichedIndex); + } }); + onIndicesLoaded(Object.values(indicesWithMetadata)); } else { console.error(enriched.error); } }) - .catch((error) => { // todo errors should be collected and displayed to the user console.error(error); }); - // todo add finally block to emit update here? }); // this is currently returning unenriched data - return Object.values(indicesWithMetadata); + // return Object.values(indicesWithMetadata); } export async function reloadIndices( diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js index f5a5679b02767..6f5725f666042 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js @@ -14,11 +14,9 @@ export const loadIndicesError = createAction('INDEX_MANAGEMENT_LOAD_INDICES_ERRO export const loadIndices = () => async (dispatch) => { dispatch(loadIndicesStart()); - let indices; try { - indices = await request(); + await request((indices) => dispatch(loadIndicesSuccess({ indices }))); } catch (error) { return dispatch(loadIndicesError(error)); } - dispatch(loadIndicesSuccess({ indices })); }; diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index b287cf7ed0bfc..2b55d3754ebd8 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -18,9 +18,8 @@ export const indexStatsEnricher = async (client: HttpSetup): Promise(`${API_BASE_PATH}/indices_stats`) .then((response) => { const indices = response.indices || {}; - return { - indices: Object.keys(indices || {}).map((name) => ({ + indices: Object.keys(indices).map((name) => ({ name, health: indices[name]?.health, status: indices[name]?.status, diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts index 008173193c44e..2849df5b8239f 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts @@ -22,14 +22,15 @@ export function registerIndicesStats({ router, lib: { handleEsError } }: RouteDe async (context, request, response) => { const { client } = (await context.core).elasticsearch; try { - const { indices } = await client.asCurrentUser.indices.stats({ + // await new Promise((resolve) => setTimeout(resolve, 10000)); + const resp = await client.asCurrentUser.indices.stats({ // todo does this need to be *,.* index: '*', // indexNamesString, expand_wildcards: ['hidden', 'all'], forbid_closed_indices: false, metric: ['docs', 'store'], }); - return response.ok({ body: indices }); + return response.ok({ body: resp }); } catch (error) { return handleEsError({ error, response }); } From 801550017299266f7c7736b6288ad3e17d306ee2 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sun, 18 Jan 2026 00:19:10 -0600 Subject: [PATCH 11/55] remove console.log --- .../shared/index_management/public/application/services/api.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 27280f9a01b87..3fc588967fba1 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -177,7 +177,6 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { // todo review types const indicesWithMetadata: Record = indices; - console.log('indicesWithMetadata', indicesWithMetadata); // iterate over all the requests for additional info enrichedPromises.forEach((enrichedPromise) => { enrichedPromise From 8699d8113975e8b6b4f279aa16323de8b9fb9910 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 21 Jan 2026 03:17:20 +0000 Subject: [PATCH 12/55] Changes from node scripts/eslint_all_files --no-cache --fix --- .../shared/index_management/public/application/services/api.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 3fc588967fba1..6e4730620596f 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ByteSizeValue } from '@kbn/config-schema'; import { METRIC_TYPE } from '@kbn/analytics'; import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import type { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; From f0793a4eec724f9b62b1e31604fd6db8398ff64f Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 20 Jan 2026 22:53:33 -0600 Subject: [PATCH 13/55] cleanup --- .../src/types.ts | 12 ++++--- .../index_management/common/types/indices.ts | 19 ++--------- .../public/application/services/api.ts | 33 +++---------------- .../public/services/index_data_enricher.ts | 2 -- 4 files changed, 14 insertions(+), 52 deletions(-) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index 8e310f7087cce..b142900eb3907 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -21,7 +21,7 @@ import type { PublicApiServiceSetup } from './services/public_api_service'; export interface EnricherResponse { source: string; - indices?: Partial[]; + indices?: Index[]; error?: string; } export type Enricher = (client: HttpSetup) => Promise; @@ -115,13 +115,15 @@ export interface IndexManagementPluginStart { }) => React.FC; } -export interface Index { +export interface Index extends IndexAttributes { name: string; +} +export interface IndexAttributes { primary?: number | string; replica?: number | string; - isFrozen: boolean; - hidden: boolean; - aliases: string | string[]; + isFrozen?: boolean; + hidden?: boolean; + aliases?: string | string[]; data_stream?: string; mode?: string; diff --git a/x-pack/platform/plugins/shared/index_management/common/types/indices.ts b/x-pack/platform/plugins/shared/index_management/common/types/indices.ts index 3254057c6c248..5f87d5a144245 100644 --- a/x-pack/platform/plugins/shared/index_management/common/types/indices.ts +++ b/x-pack/platform/plugins/shared/index_management/common/types/indices.ts @@ -7,8 +7,8 @@ import type { IndicesIndexSettingsKeys } from '@elastic/elasticsearch/lib/api/types'; -export type { Index } from '@kbn/index-management-shared-types'; - +import type { Index } from '@kbn/index-management-shared-types'; +export type { Index }; interface AnalysisModule { analyzer: { [key: string]: { @@ -32,19 +32,6 @@ export interface IndexSettingsResponse { defaults: IndexSettings; } -export interface IndexData { - documents?: number; - size?: string; - name: string; - isFrozen: boolean; - aliases?: string[]; - hidden: boolean; - data_stream?: string; - mode?: string; - primary?: number; - replica?: number; -} - export interface IndexDataResponse { - body: IndexData[]; + body: Index[]; } diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 6e4730620596f..53f24506f22af 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -48,7 +48,6 @@ import { httpService } from './http'; import type { UiMetricService } from './ui_metric'; import type { FieldFromIndicesRequest } from '../../../common'; import type { Fields } from '../components/mappings_editor/types'; -import type { IndexData } from '../../../common/types/indices'; import type { UserStartPrivilegesResponse } from '../../../server/lib/types'; import { indexDataEnricher } from '../../services'; @@ -142,24 +141,6 @@ export async function updateDSFailureStore( }); } -interface IndexMetadata { - name: string; - primary?: Number; - replica?: Number; - isFrozen: boolean; - aliases?: string[]; - hidden: boolean; - data_stream?: string; - mode?: string; - health?: string; - status?: string; - uuid?: string; - documents?: number; - documents_deleted?: number; - size?: string; - primary_size?: string; -} - export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { // Run all requests in parallel const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); @@ -167,15 +148,12 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { // we'll wait for the main request to complete first so // the index list has stability - unsure if this is an actual // issue but its certainly possible looking at the code - const indices = await httpService.httpClient.get>( + const indices = await httpService.httpClient.get>( `${API_BASE_PATH}/indices_get` ); onIndicesLoaded(Object.values(indices)); - // todo review types - const indicesWithMetadata: Record = indices; - // iterate over all the requests for additional info enrichedPromises.forEach((enrichedPromise) => { enrichedPromise @@ -183,11 +161,11 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { // iterate over the array of additional data and merge it into the original index data if (enriched.indices) { enriched.indices.forEach((enrichedIndex) => { - if (indicesWithMetadata[enrichedIndex.name]) { - Object.assign(indicesWithMetadata[enrichedIndex.name], enrichedIndex); + if (indices[enrichedIndex.name]) { + Object.assign(indices[enrichedIndex.name], enrichedIndex); } }); - onIndicesLoaded(Object.values(indicesWithMetadata)); + onIndicesLoaded(Object.values(indices)); } else { console.error(enriched.error); } @@ -197,9 +175,6 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { console.error(error); }); }); - - // this is currently returning unenriched data - // return Object.values(indicesWithMetadata); } export async function reloadIndices( diff --git a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts index 77336706ab277..c7e3249e41309 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts @@ -19,8 +19,6 @@ export class IndexDataEnricher { public enrichIndices = ( client: HttpSetup ): Promise<{ indices?: Index[]; error?: string; errorSource?: string }>[] => { - // this is complaining about the name field - // @ts-ignore return this.enrichers.map((enricher) => enricher(client)); }; From 341e80d70e4d139b0f656d48a0d26a7b3c93ea26 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 20 Jan 2026 23:21:12 -0600 Subject: [PATCH 14/55] localize errors --- .../index_management_shared_types/src/types.ts | 2 +- .../cross_cluster_replication/public/ccr_data_enricher.ts | 7 +++++-- .../public/index_lifecycle_data_enricher.ts | 8 +++++--- .../plugins/private/rollup/public/rollup_data_enricher.ts | 8 +++++--- .../index_management/public/index_stats_enricher.ts | 8 +++++--- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index b142900eb3907..82c1b30a86588 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -22,7 +22,7 @@ import type { PublicApiServiceSetup } from './services/public_api_service'; export interface EnricherResponse { source: string; indices?: Index[]; - error?: string; + error?: boolean; } export type Enricher = (client: HttpSetup) => Promise; diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts index cf6182ac958ef..6a96cf4cdf4b6 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts @@ -8,7 +8,10 @@ import type { HttpSetup } from '@kbn/core/public'; import type { CcrFollowInfoResponse } from '@elastic/elasticsearch/lib/api/types'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; -const SOURCE = 'ccr_data_enricher'; +import { i18n } from '@kbn/i18n'; +const SOURCE = i18n.translate('xpack.indexManagement.ccrDataEnricher.source', { + defaultMessage: 'cross cluster replication', +}); export const ccrDataEnricher = async (client: HttpSetup): Promise => client @@ -24,7 +27,7 @@ export const ccrDataEnricher = async (client: HttpSetup): Promise { return { - error: 'Failed to load cross cluster replication data', + error: true, source: SOURCE, }; }); diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts index a3bec696b8c10..cc9b40e126345 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts @@ -8,8 +8,10 @@ import type { IlmExplainLifecycleResponse } from '@elastic/elasticsearch/lib/api/types'; import type { HttpSetup } from '@kbn/core/public'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; - -const SOURCE = 'index_lifecycle_data_enricher'; +import { i18n } from '@kbn/i18n'; +const SOURCE = i18n.translate('xpack.indexManagement.indexLifecycleDataEnricher.source', { + defaultMessage: 'index lifecycle', +}); export const indexLifecycleDataEnricher = async (client: HttpSetup): Promise => client @@ -25,7 +27,7 @@ export const indexLifecycleDataEnricher = async (client: HttpSetup): Promise { return { - error: 'Failed to load index lifecycle data', + error: true, source: SOURCE, }; }); diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index 179ae6d90f9ef..46e321e31fe8d 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -8,8 +8,10 @@ import type { HttpSetup } from '@kbn/core/public'; import type { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api/types'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; - -const SOURCE = 'rollup_data_enricher'; +import { i18n } from '@kbn/i18n'; +const SOURCE = i18n.translate('xpack.indexManagement.rollupDataEnricher.source', { + defaultMessage: 'rollup', +}); // todo // look at x-pack/platform/plugins/private/rollup/server/rollup_data_enricher.ts @@ -33,7 +35,7 @@ export const rollupDataEnricher = async (client: HttpSetup): Promise { return { - error: 'Failed to load rollup data', + error: true, source: SOURCE, }; }); diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index 2b55d3754ebd8..b4b971a804244 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -9,9 +9,11 @@ import type { HttpSetup } from '@kbn/core/public'; import type { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; import { ByteSizeValue } from '@kbn/config-schema'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; +import { i18n } from '@kbn/i18n'; import { API_BASE_PATH } from '../common/constants'; - -const SOURCE = 'index_stats_enricher'; +const SOURCE = i18n.translate('xpack.indexManagement.indexStatsEnricher.source', { + defaultMessage: 'index stats', +}); export const indexStatsEnricher = async (client: HttpSetup): Promise => client @@ -36,7 +38,7 @@ export const indexStatsEnricher = async (client: HttpSetup): Promise { return { - error: 'Failed to load index stats', + error: true, source: SOURCE, }; }); From 7dbbc4fba58726573d1692181e34918df3d0b69b Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 20 Jan 2026 23:21:45 -0600 Subject: [PATCH 15/55] comment cleanup --- .../index_management/public/application/services/api.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 53f24506f22af..e690e56898340 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -145,9 +145,7 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { // Run all requests in parallel const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); - // we'll wait for the main request to complete first so - // the index list has stability - unsure if this is an actual - // issue but its certainly possible looking at the code + // we'll wait for the main request to complete first so the index list has stability const indices = await httpService.httpClient.get>( `${API_BASE_PATH}/indices_get` ); From 9728011026d0f9ab3ba0d183a81b26d1455a616d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:09:54 +0000 Subject: [PATCH 16/55] Changes from node scripts/lint_ts_projects --fix --- .../index_management_shared_types/tsconfig.json | 3 ++- .../plugins/private/cross_cluster_replication/tsconfig.json | 1 + x-pack/platform/plugins/private/rollup/tsconfig.json | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/tsconfig.json b/x-pack/platform/packages/shared/index-management/index_management_shared_types/tsconfig.json index 2f15478bd5975..67cfda437d90f 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/tsconfig.json +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/tsconfig.json @@ -19,6 +19,7 @@ "@kbn/core-application-browser", "@kbn/utility-types", "@kbn/share-plugin", - "@kbn/management-plugin" + "@kbn/management-plugin", + "@kbn/core" ] } diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/tsconfig.json b/x-pack/platform/plugins/private/cross_cluster_replication/tsconfig.json index b0dad4e18cf09..383c6df2a9f6f 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/tsconfig.json +++ b/x-pack/platform/plugins/private/cross_cluster_replication/tsconfig.json @@ -32,6 +32,7 @@ "@kbn/react-kibana-context-render", "@kbn/licensing-types", "@kbn/data-views-plugin", + "@kbn/index-management-shared-types", ], "exclude": [ "target/**/*", diff --git a/x-pack/platform/plugins/private/rollup/tsconfig.json b/x-pack/platform/plugins/private/rollup/tsconfig.json index 7be6e059e980d..c8a9885135f95 100644 --- a/x-pack/platform/plugins/private/rollup/tsconfig.json +++ b/x-pack/platform/plugins/private/rollup/tsconfig.json @@ -36,6 +36,7 @@ "@kbn/rollup", "@kbn/licensing-types", "@kbn/data-view-validation", + "@kbn/index-management-shared-types", ], "exclude": [ From 91e3bd5392ce707fa8448450338537d289b6dc0d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:20:59 +0000 Subject: [PATCH 17/55] Changes from node scripts/regenerate_moon_projects.js --update --- .../index-management/index_management_shared_types/moon.yml | 1 + .../platform/plugins/private/cross_cluster_replication/moon.yml | 1 + x-pack/platform/plugins/private/rollup/moon.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/moon.yml b/x-pack/platform/packages/shared/index-management/index_management_shared_types/moon.yml index e1121b2f19bdd..9849668c334ad 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/moon.yml +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/moon.yml @@ -22,6 +22,7 @@ dependsOn: - '@kbn/utility-types' - '@kbn/share-plugin' - '@kbn/management-plugin' + - '@kbn/core' tags: - shared-common - package diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/moon.yml b/x-pack/platform/plugins/private/cross_cluster_replication/moon.yml index 62863a1f0ac41..8b23fd70580c2 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/moon.yml +++ b/x-pack/platform/plugins/private/cross_cluster_replication/moon.yml @@ -38,6 +38,7 @@ dependsOn: - '@kbn/react-kibana-context-render' - '@kbn/licensing-types' - '@kbn/data-views-plugin' + - '@kbn/index-management-shared-types' tags: - plugin - prod diff --git a/x-pack/platform/plugins/private/rollup/moon.yml b/x-pack/platform/plugins/private/rollup/moon.yml index 7e7be20e01fc3..8fa4b750b3337 100644 --- a/x-pack/platform/plugins/private/rollup/moon.yml +++ b/x-pack/platform/plugins/private/rollup/moon.yml @@ -41,6 +41,7 @@ dependsOn: - '@kbn/rollup' - '@kbn/licensing-types' - '@kbn/data-view-validation' + - '@kbn/index-management-shared-types' tags: - plugin - prod From becc69579811ae80ee0ea59dafef3b4fd380797f Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 21 Jan 2026 11:39:26 -0600 Subject: [PATCH 18/55] type fixes --- .../index_management/public/application/services/api.ts | 1 + .../index_management/public/services/extensions_service.ts | 4 ++-- .../public/services/index_data_enricher.ts | 7 ++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index e690e56898340..6c370b869ced5 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -165,6 +165,7 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { }); onIndicesLoaded(Object.values(indices)); } else { + // todo need to add these to state console.error(enriched.error); } }) diff --git a/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.ts b/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.ts index 8641ca370949c..7dfa7e63287ca 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.ts @@ -24,7 +24,7 @@ export class ExtensionsService { private _badges: IndexBadge[] = [ { matchIndex: (index) => { - return index.isFrozen; + return !!index.isFrozen; }, label: i18n.translate('xpack.idxMgmt.frozenBadgeLabel', { defaultMessage: 'Frozen', @@ -36,7 +36,7 @@ export class ExtensionsService { private _toggles: IndexToggle[] = [ { matchIndex: (index) => { - return index.hidden; + return !!index.hidden; }, label: i18n.translate('xpack.idxMgmt.indexTable.hiddenIndicesSwitchLabel', { defaultMessage: 'Include hidden indices', diff --git a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts index c7e3249e41309..fd4df999208a4 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts @@ -6,8 +6,7 @@ */ import type { HttpSetup } from '@kbn/core/public'; -import type { Enricher } from '@kbn/index-management-shared-types'; -import type { Index } from '..'; +import type { Enricher, EnricherResponse } from '@kbn/index-management-shared-types'; export class IndexDataEnricher { private readonly _enrichers: Enricher[] = []; @@ -16,9 +15,7 @@ export class IndexDataEnricher { this._enrichers.push(enricher); } - public enrichIndices = ( - client: HttpSetup - ): Promise<{ indices?: Index[]; error?: string; errorSource?: string }>[] => { + public enrichIndices = (client: HttpSetup): Promise[] => { return this.enrichers.map((enricher) => enricher(client)); }; From fbebc8677bb0aa3db1a16285389c22661799b512 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 21 Jan 2026 12:51:04 -0600 Subject: [PATCH 19/55] type fix and remove console.log statements --- .../public/application/services/api.ts | 4 +- .../server/routes/api/indices/indices_get.ts | 59 ++++++++++--------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 6c370b869ced5..750c150570516 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -166,12 +166,12 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { onIndicesLoaded(Object.values(indices)); } else { // todo need to add these to state - console.error(enriched.error); + // console.error(enriched.error); } }) .catch((error) => { // todo errors should be collected and displayed to the user - console.error(error); + // console.error(error); }); }); } diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts index d6eea4003638d..6320218e5d1de 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts @@ -8,7 +8,7 @@ import { ByteSizeValue } from '@kbn/config-schema'; import type { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; import type { MeteringStatsResponse } from '../../../lib/types'; -import type { IndexData } from '../../../../common/types/indices'; +import type { Index } from '../../../../common/types/indices'; export function registerIndicesGet({ router, lib: { handleEsError }, config }: RouteDependencies) { router.get( @@ -63,39 +63,40 @@ export function registerIndicesGet({ router, lib: { handleEsError }, config }: R statsPromise, ]); - const indexStatsMap = indicesStats.reduce((prev, index) => { + const indexStatsMap = indicesStats.reduce< + Record + >((prev, index) => { prev[index.name] = { size_in_bytes: index.size_in_bytes, num_docs: index.num_docs }; return prev; - }, {} as Record); + }, {}); - const mappedIndices: Record = Object.keys(indices).reduce( - (prev, indexName: string) => { - const indexData = indices[indexName]; - const aliases = Object.keys(indexData.aliases!); - prev[indexName] = { - name: indexName, - primary: Number(indexData.settings?.index?.number_of_shards), - replica: Number(indexData.settings?.index?.number_of_replicas), - isFrozen: indexData.settings?.index?.frozen === 'true', - hidden: indexData.settings?.index?.hidden === 'true', - data_stream: indexData.data_stream, - mode: indexData.settings?.index?.mode, - }; + const mappedIndices: Record = Object.keys(indices).reduce< + Record + >((prev, indexName: string) => { + const indexData = indices[indexName]; + const aliases = Object.keys(indexData.aliases!); + prev[indexName] = { + name: indexName, + primary: Number(indexData.settings?.index?.number_of_shards), + replica: Number(indexData.settings?.index?.number_of_replicas), + isFrozen: indexData.settings?.index?.frozen === 'true', + hidden: indexData.settings?.index?.hidden === 'true', + data_stream: indexData.data_stream, + mode: indexData.settings?.index?.mode, + }; - if (aliases.length) { - prev[indexName].aliases = aliases; - } + if (aliases.length) { + prev[indexName].aliases = aliases; + } - if (indexStatsMap[indexName]) { - prev[indexName].documents = indexStatsMap[indexName].num_docs ?? 0; - prev[indexName].size = new ByteSizeValue( - indexStatsMap[indexName].size_in_bytes ?? 0 - ).toString(); - } - return prev; - }, - {} as Record - ); + if (indexStatsMap[indexName]) { + prev[indexName].documents = indexStatsMap[indexName].num_docs ?? 0; + prev[indexName].size = new ByteSizeValue( + indexStatsMap[indexName].size_in_bytes ?? 0 + ).toString(); + } + return prev; + }, {}); return response.ok({ body: mappedIndices }); } catch (error) { From 3d34bd824ba2108742cca0a383d2e51e129f0909 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 21 Jan 2026 16:45:34 -0600 Subject: [PATCH 20/55] swap formatter as to address bundle size problem --- .../public/ccr_data_enricher.ts | 2 +- .../public/index_stats_enricher.ts | 27 +++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts index 6a96cf4cdf4b6..a8fdc4652055b 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts @@ -9,7 +9,7 @@ import type { HttpSetup } from '@kbn/core/public'; import type { CcrFollowInfoResponse } from '@elastic/elasticsearch/lib/api/types'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; import { i18n } from '@kbn/i18n'; -const SOURCE = i18n.translate('xpack.indexManagement.ccrDataEnricher.source', { +const SOURCE = i18n.translate('xpack.crossClusterReplication.ccrDataEnricher.source', { defaultMessage: 'cross cluster replication', }); diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index b4b971a804244..19daf2a90e690 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -7,14 +7,31 @@ import type { HttpSetup } from '@kbn/core/public'; import type { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; -import { ByteSizeValue } from '@kbn/config-schema'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; import { i18n } from '@kbn/i18n'; +import numeral from '@elastic/numeral'; import { API_BASE_PATH } from '../common/constants'; const SOURCE = i18n.translate('xpack.indexManagement.indexStatsEnricher.source', { defaultMessage: 'index stats', }); +const formatByteSize = (valueInBytes: number): string => { + // Keep the same output shape as `ByteSizeValue.toString()` (e.g. `248b`, `6.36kb`, `2.35mb`). + let value = valueInBytes; + let unit: 'b' | 'kb' | 'mb' | 'gb' = 'b'; + + for (const nextUnit of ['kb', 'mb', 'gb'] as const) { + if (value < 1024) { + break; + } + value = value / 1024; + unit = nextUnit; + } + + const prettyValue = Number(value.toFixed(2)); + return `${prettyValue}${unit}`; +}; + export const indexStatsEnricher = async (client: HttpSetup): Promise => client .get(`${API_BASE_PATH}/indices_stats`) @@ -27,11 +44,11 @@ export const indexStatsEnricher = async (client: HttpSetup): Promise Date: Wed, 21 Jan 2026 17:47:36 -0600 Subject: [PATCH 21/55] swap formatter as to address bundle size problem --- .../public/index_stats_enricher.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index 19daf2a90e690..302c74bf4ca55 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -15,23 +15,6 @@ const SOURCE = i18n.translate('xpack.indexManagement.indexStatsEnricher.source', defaultMessage: 'index stats', }); -const formatByteSize = (valueInBytes: number): string => { - // Keep the same output shape as `ByteSizeValue.toString()` (e.g. `248b`, `6.36kb`, `2.35mb`). - let value = valueInBytes; - let unit: 'b' | 'kb' | 'mb' | 'gb' = 'b'; - - for (const nextUnit of ['kb', 'mb', 'gb'] as const) { - if (value < 1024) { - break; - } - value = value / 1024; - unit = nextUnit; - } - - const prettyValue = Number(value.toFixed(2)); - return `${prettyValue}${unit}`; -}; - export const indexStatsEnricher = async (client: HttpSetup): Promise => client .get(`${API_BASE_PATH}/indices_stats`) From a46c7e67f7d1d4b09dda183522160c9f54c146bb Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 21 Jan 2026 19:10:05 -0600 Subject: [PATCH 22/55] cleanup --- .../public/plugin.ts | 1 - .../public/application/services/api.ts | 33 ++++++++----------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts index 4a02fe0c12b3f..35d0115727def 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/plugin.ts @@ -105,7 +105,6 @@ export class CrossClusterReplicationPlugin implements Plugin { }; indexManagement.extensionsService.addBadge(followerBadgeExtension); - // note this isn't disabled if license changes indexManagement.indexDataEnricher.add(ccrDataEnricher); } } else { diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 750c150570516..e0feba2f82b7c 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -154,25 +154,20 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { // iterate over all the requests for additional info enrichedPromises.forEach((enrichedPromise) => { - enrichedPromise - .then((enriched) => { - // iterate over the array of additional data and merge it into the original index data - if (enriched.indices) { - enriched.indices.forEach((enrichedIndex) => { - if (indices[enrichedIndex.name]) { - Object.assign(indices[enrichedIndex.name], enrichedIndex); - } - }); - onIndicesLoaded(Object.values(indices)); - } else { - // todo need to add these to state - // console.error(enriched.error); - } - }) - .catch((error) => { - // todo errors should be collected and displayed to the user - // console.error(error); - }); + enrichedPromise.then((enriched) => { + // iterate over the array of additional data and merge it into the original index data + if (enriched.indices) { + enriched.indices.forEach((enrichedIndex) => { + if (indices[enrichedIndex.name]) { + Object.assign(indices[enrichedIndex.name], enrichedIndex); + } + }); + onIndicesLoaded(Object.values(indices)); + } else { + // todo need to add these to state + // console.error(enriched.error); + } + }); }); } From 11f1fab30ed499def0154e7047309f5d872f4e71 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 21 Jan 2026 22:31:57 -0600 Subject: [PATCH 23/55] i18n fixes --- .../public/index_lifecycle_data_enricher.ts | 2 +- .../plugins/private/rollup/public/rollup_data_enricher.ts | 2 +- .../shared/index_management/public/index_stats_enricher.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts index cc9b40e126345..a7c5f921cd534 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts @@ -9,7 +9,7 @@ import type { IlmExplainLifecycleResponse } from '@elastic/elasticsearch/lib/api import type { HttpSetup } from '@kbn/core/public'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; import { i18n } from '@kbn/i18n'; -const SOURCE = i18n.translate('xpack.indexManagement.indexLifecycleDataEnricher.source', { +const SOURCE = i18n.translate('xpack.indexLifecycleMgmt.indexLifecycleDataEnricher.source', { defaultMessage: 'index lifecycle', }); diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index 46e321e31fe8d..bf26ede792bea 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -9,7 +9,7 @@ import type { HttpSetup } from '@kbn/core/public'; import type { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api/types'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; import { i18n } from '@kbn/i18n'; -const SOURCE = i18n.translate('xpack.indexManagement.rollupDataEnricher.source', { +const SOURCE = i18n.translate('xpack.rollupJobs.rollupDataEnricher.source', { defaultMessage: 'rollup', }); diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index 302c74bf4ca55..6fd8e162197ad 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -11,7 +11,7 @@ import type { EnricherResponse } from '@kbn/index-management-shared-types'; import { i18n } from '@kbn/i18n'; import numeral from '@elastic/numeral'; import { API_BASE_PATH } from '../common/constants'; -const SOURCE = i18n.translate('xpack.indexManagement.indexStatsEnricher.source', { +const SOURCE = i18n.translate('xpack.idxMgmt.indexStatsEnricher.source', { defaultMessage: 'index stats', }); From ace72c49e5b1c3dbda8d5c50d8b75e382e9d0202 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 22 Jan 2026 09:38:34 -0600 Subject: [PATCH 24/55] test fixes --- .../helpers/http_requests.ts | 30 +++++++++++++++++-- .../home/data_streams_tab.test.ts | 8 ++--- .../home/indices_tab.test.tsx | 4 +-- .../mappings_editor.test.tsx | 2 +- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts index b17695ba5a958..f71e70e9c055b 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts @@ -66,8 +66,34 @@ const registerHttpRequestMockHelpers = ( const setLoadTemplatesResponse = (response?: HttpResponse, error?: ResponseError) => mockResponse('GET', `${API_BASE_PATH}/index_templates`, response, error); - const setLoadIndicesResponse = (response?: HttpResponse, error?: ResponseError) => - mockResponse('GET', `${API_BASE_PATH}/indices`, response, error); + /** + * The indices list endpoint switched from returning an array (`/indices`) to returning + * a record keyed by index name (`/indices_get`). Most tests still pass an array, so we + * normalize here and mock both endpoints for compatibility. + */ + const setLoadIndicesResponse = (response?: HttpResponse, error?: ResponseError) => { + const normalizedIndicesGetResponse = (() => { + if (!response) return response; + if (Array.isArray(response)) { + return response.reduce>((acc, index: any) => { + if (index?.name) acc[index.name] = index; + return acc; + }, {}); + } + return response; + })(); + + // New endpoint (record keyed by index name) + mockResponse('GET', `${API_BASE_PATH}/indices_get`, normalizedIndicesGetResponse, error); + + // Legacy endpoint (array) - keep for older consumers/tests + const legacyResponse = Array.isArray(response) + ? response + : response && typeof response === 'object' + ? Object.values(response as Record) + : response; + mockResponse('GET', `${API_BASE_PATH}/indices`, legacyResponse, error); + }; const setReloadIndicesResponse = (response?: HttpResponse, error?: ResponseError) => mockResponse('POST', `${API_BASE_PATH}/indices/reload`, response, error); diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/data_streams_tab.test.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/data_streams_tab.test.ts index 746eefa5c2a3b..1fbaec2e48c01 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/data_streams_tab.test.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/data_streams_tab.test.ts @@ -790,7 +790,7 @@ describe('Data Streams tab', () => { }) ); }); - }); + }, 10000); test('allows to set infinite retention period', async () => { setupBulkRetentionMocks(); @@ -833,7 +833,7 @@ describe('Data Streams tab', () => { }) ); }); - }); + }, 10000); }); describe('detail panel', () => { @@ -1004,7 +1004,7 @@ describe('Data Streams tab', () => { }) ); }); - }); + }, 10000); test('can disable lifecycle', async () => { setupDataStreamsMocks(); @@ -1047,7 +1047,7 @@ describe('Data Streams tab', () => { }) ); }); - }); + }, 10000); test('allows to set infinite retention period', async () => { setupDataStreamsMocks(); diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx index ceaa86c5112ea..04e47561b721b 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx @@ -462,7 +462,7 @@ describe('', () => { const actions = createCreateIndexActions(); expect(httpSetup.get).toHaveBeenCalledTimes(1); - expect(httpSetup.get).toHaveBeenNthCalledWith(1, '/api/index_management/indices'); + expect(httpSetup.get).toHaveBeenNthCalledWith(1, '/api/index_management/indices_get'); await actions.clickCreateIndexButton(); @@ -482,7 +482,7 @@ describe('', () => { // It refreshes indices after saving; wait so the table's async state update settles (avoids act warnings). await waitFor(() => { expect(httpSetup.get).toHaveBeenCalledTimes(2); - expect(httpSetup.get).toHaveBeenNthCalledWith(2, '/api/index_management/indices'); + expect(httpSetup.get).toHaveBeenNthCalledWith(2, '/api/index_management/indices_get'); }); // Creating triggers modal state updates; wait for modal to close so updates don't land after test end. diff --git a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx index 3ee1a7d7c3e57..6a1c059496c9c 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx @@ -453,7 +453,7 @@ describe('Mappings editor: core', () => { expect(isDynamicMappingsEnabled).toBe(false); isNumericDetectionVisible = screen.queryByTestId('numericDetection'); expect(isNumericDetectionVisible).not.toBeInTheDocument(); - }); + }, 10000); test('should keep default dynamic templates value when switching tabs', async () => { setup( From ee9a91be4d010764b87dbd45d9363da20a2e36d2 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sat, 24 Jan 2026 20:35:09 -0600 Subject: [PATCH 25/55] show loading errors --- .../index_table/index_table.container.js | 2 ++ .../index_list/index_table/index_table.js | 32 +++++++++++++++++++ .../public/application/services/api.ts | 13 +++++--- .../application/store/actions/load_indices.js | 8 ++++- .../application/store/reducers/indices.js | 23 +++++++++++++ .../application/store/selectors/index.js | 1 + 6 files changed, 74 insertions(+), 5 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.container.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.container.js index 66406576902c2..3fb41b564e535 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.container.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.container.js @@ -16,6 +16,7 @@ import { getIndicesAsArray, indicesLoading, indicesError, + indicesEnrichmentErrors, getTableState, } from '../../../../store/selectors'; import { @@ -40,6 +41,7 @@ const mapStateToProps = (state, props) => { isSortAscending: isSortAscending(state), indicesLoading: indicesLoading(state), indicesError: indicesError(state), + indicesEnrichmentErrors: indicesEnrichmentErrors(state), toggleNameToVisibleMap: getTableState(state).toggleNameToVisibleMap, }; }; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index 37c257995f63c..9b2f55b5fa350 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -301,6 +301,35 @@ export class IndexTable extends Component { ); } + renderEnrichmentErrors() { + const { indicesEnrichmentErrors } = this.props; + if (!indicesEnrichmentErrors || indicesEnrichmentErrors.length === 0) { + return null; + } + + return ( + <> + + + + + + ); + } + onFilterChanged = ({ query, error }) => { if (error) { this.setState({ filterError: error }); @@ -520,6 +549,7 @@ export class IndexTable extends Component { loadIndices, indicesLoading, indicesError, + indicesEnrichmentErrors, allIndices, pager, pageChanged, @@ -707,6 +737,8 @@ export class IndexTable extends Component { {this.renderFilterError()} + {this.renderEnrichmentErrors()} +
diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index e0feba2f82b7c..c65cc62674be4 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -141,7 +141,10 @@ export async function updateDSFailureStore( }); } -export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { +export async function loadIndices( + onIndicesLoaded: (indices: Index[]) => void, + onEnrichmentError: (source: string) => void +) { // Run all requests in parallel const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); @@ -163,9 +166,11 @@ export async function loadIndices(onIndicesLoaded: (indices: Index[]) => void) { } }); onIndicesLoaded(Object.values(indices)); - } else { - // todo need to add these to state - // console.error(enriched.error); + } + + // If an enricher fails, keep the index list stable but surface the issue to the UI. + if (enriched.error) { + onEnrichmentError(enriched.source); } }); }); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js index 6f5725f666042..5ce8cdcd4607e 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js @@ -11,11 +11,17 @@ import { loadIndices as request } from '../../services'; export const loadIndicesStart = createAction('INDEX_MANAGEMENT_LOAD_INDICES_START'); export const loadIndicesSuccess = createAction('INDEX_MANAGEMENT_LOAD_INDICES_SUCCESS'); export const loadIndicesError = createAction('INDEX_MANAGEMENT_LOAD_INDICES_ERROR'); +export const loadIndicesEnrichmentError = createAction( + 'INDEX_MANAGEMENT_LOAD_INDICES_ENRICHMENT_ERROR' +); export const loadIndices = () => async (dispatch) => { dispatch(loadIndicesStart()); try { - await request((indices) => dispatch(loadIndicesSuccess({ indices }))); + await request( + (indices) => dispatch(loadIndicesSuccess({ indices })), + (source) => dispatch(loadIndicesEnrichmentError({ source })) + ); } catch (error) { return dispatch(loadIndicesError(error)); } diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/reducers/indices.js b/x-pack/platform/plugins/shared/index_management/public/application/store/reducers/indices.js index a52e5638de7e1..78ba352bdbe6c 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/reducers/indices.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/reducers/indices.js @@ -13,6 +13,7 @@ import { reloadIndicesSuccess, loadIndicesStart, loadIndicesError, + loadIndicesEnrichmentError, } from '../actions'; const byId = handleActions( @@ -101,13 +102,35 @@ const error = handleActions( const newState = { ...error }; return newState; }, + [loadIndicesStart]() { + return false; + }, + [loadIndicesSuccess]() { + return false; + }, }, false ); +const enrichmentErrors = handleActions( + { + [loadIndicesStart]() { + return []; + }, + [loadIndicesEnrichmentError](state, action) { + const { source } = action.payload; + if (!source) return state; + if (state.includes(source)) return state; + return [...state, source]; + }, + }, + [] +); + export const indices = combineReducers({ loading, error, + enrichmentErrors, byId, allIds, }); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/selectors/index.js b/x-pack/platform/plugins/shared/index_management/public/application/store/selectors/index.js index 2e1e01f3122ae..d4bf1e1dbd0c0 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/selectors/index.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/selectors/index.js @@ -17,6 +17,7 @@ export { extensionsService }; export const getIndices = (state) => state.indices.byId; export const indicesLoading = (state) => state.indices.loading; export const indicesError = (state) => state.indices.error; +export const indicesEnrichmentErrors = (state) => state.indices.enrichmentErrors; export const getIndicesAsArray = (state) => Object.values(state.indices.byId); export const getIndicesByName = (state, indexNames) => { const indices = getIndices(state); From 029b1d8306632e6b753fff16fe95016d7793f572 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sat, 24 Jan 2026 21:00:58 -0600 Subject: [PATCH 26/55] add tests for displaying errors --- .../helpers/http_requests.ts | 4 ++++ .../home/indices_tab.test.tsx | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts index f71e70e9c055b..bb0925741fa90 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/http_requests.ts @@ -66,6 +66,9 @@ const registerHttpRequestMockHelpers = ( const setLoadTemplatesResponse = (response?: HttpResponse, error?: ResponseError) => mockResponse('GET', `${API_BASE_PATH}/index_templates`, response, error); + const setLoadIndicesStatsResponse = (response?: HttpResponse, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/indices_stats`, response, error); + /** * The indices list endpoint switched from returning an array (`/indices`) to returning * a record keyed by index name (`/indices_get`). Most tests still pass an array, so we @@ -247,6 +250,7 @@ const registerHttpRequestMockHelpers = ( }; return { setLoadTemplatesResponse, + setLoadIndicesStatsResponse, setLoadIndicesResponse, setReloadIndicesResponse, setLoadDataStreamsResponse, diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx index 04e47561b721b..43584145fcfc8 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx @@ -17,6 +17,7 @@ import { API_BASE_PATH, INTERNAL_API_BASE_PATH } from '../../../common'; import { setupEnvironment } from '../helpers/setup_environment'; import { renderHome } from '../helpers/render_home'; import { httpService } from '../../../public/application/services/http'; +import { indexDataEnricher } from '../../../public/services'; import { createIndexTableActions, createCreateIndexActions, @@ -421,6 +422,26 @@ describe('', () => { expect(screen.queryByTestId('indexTableCell-documents')).not.toBeInTheDocument(); expect(screen.queryByTestId('indexTableCell-size')).not.toBeInTheDocument(); }); + + test('shows a warning callout when an index enricher fails', async () => { + httpRequestsMockHelpers.setLoadIndicesResponse([createNonDataStreamIndex(indexName)]); + const originalEnrichers = [...indexDataEnricher.enrichers]; + indexDataEnricher.add(async () => ({ source: 'test enricher', error: true })); + + try { + await renderHome(httpSetup); + + await screen.findByTestId('indexTable'); + expect(screen.getByTestId('indexTableCell-name')).toHaveTextContent('test'); + + const callout = await screen.findByTestId('indicesEnrichmentErrorCallout'); + expect(callout).toHaveTextContent('test enricher'); + } finally { + // Restore enrichers to avoid polluting other tests. + (indexDataEnricher as any)._enrichers.length = 0; + originalEnrichers.forEach((enricher) => indexDataEnricher.add(enricher)); + } + }); }); describe('Create Index', () => { From f269013c13bcf25736845ac659f0b2c3f715096a Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sat, 24 Jan 2026 23:40:33 -0600 Subject: [PATCH 27/55] remove unused var --- .../sections/home/index_list/index_table/index_table.js | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index 9b2f55b5fa350..113ecf7264f62 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -549,7 +549,6 @@ export class IndexTable extends Component { loadIndices, indicesLoading, indicesError, - indicesEnrichmentErrors, allIndices, pager, pageChanged, From 875417e26c6184876a70533d77c785d04348d612 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 26 Jan 2026 07:20:28 -0600 Subject: [PATCH 28/55] cleanup --- .../routes/api/follower_index/register_get_follower_info.ts | 2 +- .../server/routes/api/policies/register_explain_route.ts | 2 +- .../server/routes/api/indices/indices_stats.ts | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts index b34ed314e138c..e182e3d13b24a 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_get_follower_info.ts @@ -29,7 +29,7 @@ export const registerGetFollowerInfoRoute = ({ try { const body = await client.asCurrentUser.ccr.followInfo({ - index: '*', + index: '_all', }); return response.ok({ diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts b/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts index 8bf9168447cae..c2b5753fa0bed 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/policies/register_explain_route.ts @@ -27,7 +27,7 @@ export const registerExplainRoute = ({ license.guardApiRoute(async (context, request, response) => { const { client } = (await context.core).elasticsearch; try { - const body = await client.asCurrentUser.ilm.explainLifecycle({ index: '*,.*' }); + const body = await client.asCurrentUser.ilm.explainLifecycle({ index: '_all' }); return response.ok({ body }); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts index 2849df5b8239f..788e1b39544da 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts @@ -22,10 +22,8 @@ export function registerIndicesStats({ router, lib: { handleEsError } }: RouteDe async (context, request, response) => { const { client } = (await context.core).elasticsearch; try { - // await new Promise((resolve) => setTimeout(resolve, 10000)); const resp = await client.asCurrentUser.indices.stats({ - // todo does this need to be *,.* - index: '*', // indexNamesString, + index: '*', expand_wildcards: ['hidden', 'all'], forbid_closed_indices: false, metric: ['docs', 'store'], From 4acd459cb551c6c08c5857469d469421f4898355 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 26 Jan 2026 14:09:09 -0600 Subject: [PATCH 29/55] address alias use case for rollups --- .../src/types.ts | 4 ++ .../rollup/public/rollup_data_enricher.ts | 1 + .../home/indices_tab.test.tsx | 42 +++++++++++++++++++ .../public/application/services/api.ts | 42 ++++++++++++++++++- 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index 82c1b30a86588..b0139206f5cc3 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -23,6 +23,10 @@ export interface EnricherResponse { source: string; indices?: Index[]; error?: boolean; + /** + * Apply this enricher's updates to any index aliases + */ + applyToAliases?: boolean; } export type Enricher = (client: HttpSetup) => Promise; diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index bf26ede792bea..515d29567e46f 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -21,6 +21,7 @@ export const rollupDataEnricher = async (client: HttpSetup): Promise('/api/rollup/indices') .then((response) => { return { + applyToAliases: true, indices: Object.keys(response).reduce((acc, rollupJob) => { response[rollupJob].rollup_jobs.forEach((job) => { acc.push({ diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx index 43584145fcfc8..d058fc7384e29 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx @@ -612,5 +612,47 @@ describe('', () => { expect(navigateToUrl).toHaveBeenCalledTimes(1); expect(navigateToUrl).toHaveBeenCalledWith(url); }); + + it('applies enricher updates to indices via alias when applyToAliases is true', async () => { + const indexName = 'concrete-index'; + const aliasName = 'my-alias'; + + httpRequestsMockHelpers.setLoadIndicesResponse([ + { ...createNonDataStreamIndex(indexName), aliases: [aliasName] }, + ]); + + const originalEnrichers = [...indexDataEnricher.enrichers]; + indexDataEnricher.add(async () => ({ + source: 'alias enricher', + applyToAliases: true, + indices: [{ name: aliasName, isRollupIndex: true }], + })); + + try { + await renderHome(httpSetup, { + appServicesContext: { + services: { + extensionsService: { + _columns: [ + { + fieldName: 'isRollupIndex', + label: 'Rollup flag', + order: 999, + render: (index: Index) => (index.isRollupIndex ?
ROLLUP
: null), + }, + ], + }, + }, + }, + }); + + await screen.findByTestId('indexTable'); + expect(await screen.findByText('ROLLUP')).toBeInTheDocument(); + } finally { + // Restore enrichers to avoid polluting other tests. + (indexDataEnricher as any)._enrichers.length = 0; + originalEnrichers.forEach((enricher) => indexDataEnricher.add(enricher)); + } + }); }); }); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index c65cc62674be4..cff361d7babb9 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -153,6 +153,27 @@ export async function loadIndices( `${API_BASE_PATH}/indices_get` ); + // Pre-compute an alias -> index names lookup for enrichers that return data keyed by alias. + const aliasToIndexNames = new Map(); + Object.entries(indices).forEach(([indexName, index]) => { + const aliases = index.aliases; + const aliasList = Array.isArray(aliases) + ? aliases + : typeof aliases === 'string' && aliases !== 'none' + ? [aliases] + : []; + + aliasList.forEach((alias) => { + if (!alias) return; + const existing = aliasToIndexNames.get(alias); + if (existing) { + existing.push(indexName); + } else { + aliasToIndexNames.set(alias, [indexName]); + } + }); + }); + onIndicesLoaded(Object.values(indices)); // iterate over all the requests for additional info @@ -161,8 +182,25 @@ export async function loadIndices( // iterate over the array of additional data and merge it into the original index data if (enriched.indices) { enriched.indices.forEach((enrichedIndex) => { - if (indices[enrichedIndex.name]) { - Object.assign(indices[enrichedIndex.name], enrichedIndex); + const directMatch = indices[enrichedIndex.name]; + if (directMatch) { + Object.assign(directMatch, enrichedIndex); + return; + } + + if (enriched.applyToAliases) { + const targets = aliasToIndexNames.get(enrichedIndex.name); + if (targets && targets.length) { + // Don't overwrite the concrete index name with the alias name. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { name, ...rest } = enrichedIndex; + targets.forEach((targetIndexName) => { + const target = indices[targetIndexName]; + if (target) { + Object.assign(target, rest); + } + }); + } } }); onIndicesLoaded(Object.values(indices)); From fdb62829ea9915ec7320faef586eb2f8e0cc9dd2 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:40:28 +0000 Subject: [PATCH 30/55] Changes from node scripts/eslint_all_files --no-cache --fix --- .../index_management/public/application/services/api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index cff361d7babb9..3a413cb4bca79 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -160,8 +160,8 @@ export async function loadIndices( const aliasList = Array.isArray(aliases) ? aliases : typeof aliases === 'string' && aliases !== 'none' - ? [aliases] - : []; + ? [aliases] + : []; aliasList.forEach((alias) => { if (!alias) return; @@ -192,7 +192,7 @@ export async function loadIndices( const targets = aliasToIndexNames.get(enrichedIndex.name); if (targets && targets.length) { // Don't overwrite the concrete index name with the alias name. - // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { name, ...rest } = enrichedIndex; targets.forEach((targetIndexName) => { const target = indices[targetIndexName]; From b7600ef1030518756891f1b78acc1731cc0bf229 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 26 Jan 2026 15:55:58 -0600 Subject: [PATCH 31/55] remove comment --- .../rollup/public/rollup_data_enricher.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index 515d29567e46f..57e1fcd7db33b 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -13,9 +13,6 @@ const SOURCE = i18n.translate('xpack.rollupJobs.rollupDataEnricher.source', { defaultMessage: 'rollup', }); -// todo -// look at x-pack/platform/plugins/private/rollup/server/rollup_data_enricher.ts -// account for aliases export const rollupDataEnricher = async (client: HttpSetup): Promise => client .get('/api/rollup/indices') @@ -40,25 +37,3 @@ export const rollupDataEnricher = async (client: HttpSetup): Promise { - let isRollupIndex = !!rollupJobData[index.name]; - if (!isRollupIndex && isArray(index.aliases)) { - isRollupIndex = index.aliases.some((alias) => !!rollupJobData[alias]); - } - return { - ...index, - isRollupIndex, - }; - }); - } catch (e) { - // swallow exceptions and return original list - return indicesList; - } -*/ From 8a7cf51aea7e7b7114a0148b72f8b7176fdea142 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 26 Jan 2026 20:05:41 -0600 Subject: [PATCH 32/55] fix rollup enricher --- .../rollup/public/rollup_data_enricher.ts | 2 +- .../rollup/server/routes/api/indices/index.ts | 2 + .../api/indices/register_get_index_caps.ts | 43 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 x-pack/platform/plugins/private/rollup/server/routes/api/indices/register_get_index_caps.ts diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index 57e1fcd7db33b..4c091d6f75d93 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -15,7 +15,7 @@ const SOURCE = i18n.translate('xpack.rollupJobs.rollupDataEnricher.source', { export const rollupDataEnricher = async (client: HttpSetup): Promise => client - .get('/api/rollup/indices') + .get('/api/rollup/indices_caps') .then((response) => { return { applyToAliases: true, diff --git a/x-pack/platform/plugins/private/rollup/server/routes/api/indices/index.ts b/x-pack/platform/plugins/private/rollup/server/routes/api/indices/index.ts index 7f667fd00db88..27e8a9a3929e4 100644 --- a/x-pack/platform/plugins/private/rollup/server/routes/api/indices/index.ts +++ b/x-pack/platform/plugins/private/rollup/server/routes/api/indices/index.ts @@ -8,8 +8,10 @@ import type { RouteDependencies } from '../../../types'; import { registerGetRoute } from './register_get_route'; import { registerValidateIndexPatternRoute } from './register_validate_index_pattern_route'; +import { registerGetIndexCapsRoute } from './register_get_index_caps'; export function registerIndicesRoutes(dependencies: RouteDependencies) { registerGetRoute(dependencies); registerValidateIndexPatternRoute(dependencies); + registerGetIndexCapsRoute(dependencies); } diff --git a/x-pack/platform/plugins/private/rollup/server/routes/api/indices/register_get_index_caps.ts b/x-pack/platform/plugins/private/rollup/server/routes/api/indices/register_get_index_caps.ts new file mode 100644 index 0000000000000..4e07fbc54575c --- /dev/null +++ b/x-pack/platform/plugins/private/rollup/server/routes/api/indices/register_get_index_caps.ts @@ -0,0 +1,43 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { addBasePath } from '../../../services'; +import type { RouteDependencies } from '../../../types'; + +/** + * Returns a list of all rollup index names + */ +export const registerGetIndexCapsRoute = ({ + router, + license, + lib: { handleEsError }, +}: RouteDependencies) => { + router.get( + { + // this endpoint is used by the data views plugin, see https://github.com/elastic/kibana/issues/152708 + path: addBasePath('/indices_caps'), + security: { + authz: { + enabled: false, + reason: 'Relies on es client for authorization', + }, + }, + validate: false, + }, + license.guardApiRoute(async (context, request, response) => { + try { + const { client: clusterClient } = (await context.core).elasticsearch; + const body = await clusterClient.asCurrentUser.rollup.getRollupIndexCaps({ + index: '*', + }); + return response.ok({ body }); + } catch (err) { + return handleEsError({ error: err, response }); + } + }) + ); +}; From 7afddddbcb43f94e8d51fdba00f75c339829bb5f Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 27 Jan 2026 21:38:05 -0600 Subject: [PATCH 33/55] start get indices request before enricher requests --- .../index_management/public/application/services/api.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 3a413cb4bca79..0b2d618691a21 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -145,13 +145,15 @@ export async function loadIndices( onIndicesLoaded: (indices: Index[]) => void, onEnrichmentError: (source: string) => void ) { + const indicesPromise = httpService.httpClient.get>( + `${API_BASE_PATH}/indices_get` + ); + // Run all requests in parallel const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); // we'll wait for the main request to complete first so the index list has stability - const indices = await httpService.httpClient.get>( - `${API_BASE_PATH}/indices_get` - ); + const indices = await indicesPromise; // Pre-compute an alias -> index names lookup for enrichers that return data keyed by alias. const aliasToIndexNames = new Map(); From 8fca925edba8c132c67929487df4b534a323b31c Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 27 Jan 2026 23:42:36 -0600 Subject: [PATCH 34/55] cancel requests on reload, centalized catching of errors --- .../src/types.ts | 5 +- .../public/ccr_data_enricher.ts | 33 ++++++------ .../public/index_lifecycle_data_enricher.ts | 33 ++++++------ .../rollup/public/rollup_data_enricher.ts | 43 ++++++++-------- .../home/indices_tab.test.tsx | 12 ++++- .../index_list/index_table/index_table.js | 2 +- .../public/application/services/api.ts | 17 ++++++- .../public/index_stats_enricher.ts | 51 +++++++++---------- .../public/services/index_data_enricher.ts | 17 ++++++- 9 files changed, 119 insertions(+), 94 deletions(-) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index b0139206f5cc3..3ed7ab84855cf 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -28,7 +28,10 @@ export interface EnricherResponse { */ applyToAliases?: boolean; } -export type Enricher = (client: HttpSetup) => Promise; +export interface Enricher { + name: string; + fn: (client: HttpSetup, signal: AbortSignal) => Promise; +} export type IndexManagementLocatorParams = SerializableRecord & ( diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts index a8fdc4652055b..b79f1c88af8e0 100644 --- a/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts +++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/ccr_data_enricher.ts @@ -13,21 +13,18 @@ const SOURCE = i18n.translate('xpack.crossClusterReplication.ccrDataEnricher.sou defaultMessage: 'cross cluster replication', }); -export const ccrDataEnricher = async (client: HttpSetup): Promise => - client - .get('/api/cross_cluster_replication/follower_info') - .then((response) => { - return { - indices: response.follower_indices.map((followerIndex) => ({ - name: followerIndex.follower_index, - isFollowerIndex: true, - })), - source: SOURCE, - }; - }) - .catch((error) => { - return { - error: true, - source: SOURCE, - }; - }); +export const ccrDataEnricher = { + name: SOURCE, + fn: async (client: HttpSetup, signal: AbortSignal): Promise => + client + .get('/api/cross_cluster_replication/follower_info', { signal }) + .then((response) => { + return { + indices: response.follower_indices.map((followerIndex) => ({ + name: followerIndex.follower_index, + isFollowerIndex: true, + })), + source: SOURCE, + }; + }), +}; diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts index a7c5f921cd534..302f468f65b20 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/index_lifecycle_data_enricher.ts @@ -13,21 +13,18 @@ const SOURCE = i18n.translate('xpack.indexLifecycleMgmt.indexLifecycleDataEnrich defaultMessage: 'index lifecycle', }); -export const indexLifecycleDataEnricher = async (client: HttpSetup): Promise => - client - .get('/api/index_lifecycle_management/explain') - .then((response) => { - return { - indices: Object.keys(response.indices).map((index) => ({ - name: index, - ilm: response.indices[index], - })), - source: SOURCE, - }; - }) - .catch((error) => { - return { - error: true, - source: SOURCE, - }; - }); +export const indexLifecycleDataEnricher = { + name: SOURCE, + fn: async (client: HttpSetup, signal: AbortSignal): Promise => + client + .get('/api/index_lifecycle_management/explain', { signal }) + .then((response) => { + return { + indices: Object.keys(response.indices).map((index) => ({ + name: index, + ilm: response.indices[index], + })), + source: SOURCE, + }; + }), +}; diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index 4c091d6f75d93..af163983bddf4 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -13,27 +13,24 @@ const SOURCE = i18n.translate('xpack.rollupJobs.rollupDataEnricher.source', { defaultMessage: 'rollup', }); -export const rollupDataEnricher = async (client: HttpSetup): Promise => - client - .get('/api/rollup/indices_caps') - .then((response) => { - return { - applyToAliases: true, - indices: Object.keys(response).reduce((acc, rollupJob) => { - response[rollupJob].rollup_jobs.forEach((job) => { - acc.push({ - name: job.rollup_index, - isRollupIndex: true, +export const rollupDataEnricher = { + name: SOURCE, + fn: async (client: HttpSetup, signal: AbortSignal): Promise => + client + .get('/api/rollup/indices_caps', { signal }) + .then((response) => { + return { + applyToAliases: true, + indices: Object.keys(response).reduce((acc, rollupJob) => { + response[rollupJob].rollup_jobs.forEach((job) => { + acc.push({ + name: job.rollup_index, + isRollupIndex: true, + }); }); - }); - return acc; - }, [] as { name: string; isRollupIndex: true }[]), - source: SOURCE, - }; - }) - .catch((error) => { - return { - error: true, - source: SOURCE, - }; - }); + return acc; + }, [] as { name: string; isRollupIndex: true }[]), + source: SOURCE, + }; + }), +}; diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx index d058fc7384e29..3d8a68408088a 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx @@ -483,7 +483,11 @@ describe('', () => { const actions = createCreateIndexActions(); expect(httpSetup.get).toHaveBeenCalledTimes(1); - expect(httpSetup.get).toHaveBeenNthCalledWith(1, '/api/index_management/indices_get'); + expect(httpSetup.get).toHaveBeenNthCalledWith( + 1, + '/api/index_management/indices_get', + expect.anything() + ); await actions.clickCreateIndexButton(); @@ -503,7 +507,11 @@ describe('', () => { // It refreshes indices after saving; wait so the table's async state update settles (avoids act warnings). await waitFor(() => { expect(httpSetup.get).toHaveBeenCalledTimes(2); - expect(httpSetup.get).toHaveBeenNthCalledWith(2, '/api/index_management/indices_get'); + expect(httpSetup.get).toHaveBeenNthCalledWith( + 2, + '/api/index_management/indices_get', + expect.anything() + ); }); // Creating triggers modal state updates; wait for modal to close so updates don't land after test end. diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index 113ecf7264f62..86d7a89439dd8 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -147,7 +147,7 @@ const getColumnConfigs = ({ defaultMessage: 'Health', }), order: 20, - render: (index) => , + render: (index) => (index.health ? : undefined), }, { fieldName: 'status', diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 0b2d618691a21..2a264812fc24a 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -141,16 +141,29 @@ export async function updateDSFailureStore( }); } +let abortController: AbortController; export async function loadIndices( onIndicesLoaded: (indices: Index[]) => void, onEnrichmentError: (source: string) => void ) { + if (abortController && !abortController.signal.aborted) { + abortController.abort(); + } + + abortController = new AbortController(); + const indicesPromise = httpService.httpClient.get>( - `${API_BASE_PATH}/indices_get` + `${API_BASE_PATH}/indices_get`, + { + signal: abortController.signal, + } ); // Run all requests in parallel - const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient); + const enrichedPromises = indexDataEnricher.enrichIndices( + httpService.httpClient, + abortController.signal + ); // we'll wait for the main request to complete first so the index list has stability const indices = await indicesPromise; diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index 6fd8e162197ad..9429616064563 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -15,30 +15,27 @@ const SOURCE = i18n.translate('xpack.idxMgmt.indexStatsEnricher.source', { defaultMessage: 'index stats', }); -export const indexStatsEnricher = async (client: HttpSetup): Promise => - client - .get(`${API_BASE_PATH}/indices_stats`) - .then((response) => { - const indices = response.indices || {}; - return { - indices: Object.keys(indices).map((name) => ({ - name, - health: indices[name]?.health, - status: indices[name]?.status, - uuid: indices[name]?.uuid, - documents_deleted: indices[name]?.primaries?.docs?.deleted ?? 0, - primary_size: numeral(indices[name]?.primaries?.store?.size_in_bytes ?? 0).format( - '0.00 b' - ), - documents: indices[name]?.primaries?.docs?.count ?? 0, - size: numeral(indices[name]?.total?.store?.size_in_bytes ?? 0).format('0.00 b'), - })), - source: SOURCE, - }; - }) - .catch((error) => { - return { - error: true, - source: SOURCE, - }; - }); +export const indexStatsEnricher = { + name: SOURCE, + fn: async (client: HttpSetup, signal: AbortSignal): Promise => + client + .get(`${API_BASE_PATH}/indices_stats`, { signal }) + .then(async (response) => { + const indices = response.indices || {}; + return { + indices: Object.keys(indices).map((name) => ({ + name, + health: indices[name]?.health, + status: indices[name]?.status, + uuid: indices[name]?.uuid, + documents_deleted: indices[name]?.primaries?.docs?.deleted ?? 0, + primary_size: numeral(indices[name]?.primaries?.store?.size_in_bytes ?? 0).format( + '0.00 b' + ), + documents: indices[name]?.primaries?.docs?.count ?? 0, + size: numeral(indices[name]?.total?.store?.size_in_bytes ?? 0).format('0.00 b'), + })), + source: SOURCE, + }; + }), +}; diff --git a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts index fd4df999208a4..50cecb12ca15f 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/index_data_enricher.ts @@ -15,8 +15,21 @@ export class IndexDataEnricher { this._enrichers.push(enricher); } - public enrichIndices = (client: HttpSetup): Promise[] => { - return this.enrichers.map((enricher) => enricher(client)); + public enrichIndices = (client: HttpSetup, signal: AbortSignal): Promise[] => { + return this.enrichers.map((enricher) => + enricher.fn(client, signal).catch((error) => { + // aborted request, dont show error + if (error.name === 'AbortError') { + return { + source: enricher.name, + }; + } + return { + error: true, + source: enricher.name, + }; + }) + ); }; public get enrichers() { From 5e1bebeafa161bf6a838ae17db8fc35f0ad3c2b5 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 00:15:58 -0600 Subject: [PATCH 35/55] update indices tab test --- .../home/indices_tab.test.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx index 3d8a68408088a..a80ca90771aae 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx @@ -426,7 +426,10 @@ describe('', () => { test('shows a warning callout when an index enricher fails', async () => { httpRequestsMockHelpers.setLoadIndicesResponse([createNonDataStreamIndex(indexName)]); const originalEnrichers = [...indexDataEnricher.enrichers]; - indexDataEnricher.add(async () => ({ source: 'test enricher', error: true })); + indexDataEnricher.add({ + name: 'test enricher', + fn: async () => ({ source: 'test enricher', error: true }), + }); try { await renderHome(httpSetup); @@ -630,11 +633,14 @@ describe('', () => { ]); const originalEnrichers = [...indexDataEnricher.enrichers]; - indexDataEnricher.add(async () => ({ - source: 'alias enricher', - applyToAliases: true, - indices: [{ name: aliasName, isRollupIndex: true }], - })); + indexDataEnricher.add({ + name: 'alias enricher', + fn: async () => ({ + source: 'alias enricher', + applyToAliases: true, + indices: [{ name: aliasName, isRollupIndex: true }], + }), + }); try { await renderHome(httpSetup, { From 9c75013bc6785c3b2a847d94d63867370d281220 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 12:36:22 -0600 Subject: [PATCH 36/55] redue index stats response --- .../server/routes/api/indices/indices_stats.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts index 788e1b39544da..bbbc70e186333 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts @@ -27,6 +27,15 @@ export function registerIndicesStats({ router, lib: { handleEsError } }: RouteDe expand_wildcards: ['hidden', 'all'], forbid_closed_indices: false, metric: ['docs', 'store'], + filter_path: [ + '*.*.uuid', + '*.*.status', + '*.*.health', + '*.*.primaries.docs.count', + '*.*.primaries.docs.deleted', + '*.*.total.store.size_in_bytes', + '*.*.primaries.store.size_in_bytes', + ], }); return response.ok({ body: resp }); } catch (error) { From 631d247988bea7462f11824d8242458b2fbd101f Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 13:12:59 -0600 Subject: [PATCH 37/55] address abortError for main index fetch --- .../public/application/services/api.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 2a264812fc24a..371a3241ae418 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -166,7 +166,16 @@ export async function loadIndices( ); // we'll wait for the main request to complete first so the index list has stability - const indices = await indicesPromise; + const indices = await indicesPromise.catch((error) => { + if (error.name === 'AbortError') { + // return undefined and exit early if the request was aborted + return; + } + }); + + if (!indices) { + return; + } // Pre-compute an alias -> index names lookup for enrichers that return data keyed by alias. const aliasToIndexNames = new Map(); From 1773baf172064e3390b1e779fa7f0a048ccc2cce Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 15:02:20 -0600 Subject: [PATCH 38/55] move abort signal up one level --- .../public/application/services/api.ts | 17 +++-------- .../application/store/actions/load_indices.js | 28 ------------------- .../public/index_stats_enricher.ts | 2 +- 3 files changed, 5 insertions(+), 42 deletions(-) delete mode 100644 x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 371a3241ae418..2d8365d184414 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -141,29 +141,20 @@ export async function updateDSFailureStore( }); } -let abortController: AbortController; export async function loadIndices( onIndicesLoaded: (indices: Index[]) => void, - onEnrichmentError: (source: string) => void + onEnrichmentError: (source: string) => void, + abortSignal: AbortSignal ) { - if (abortController && !abortController.signal.aborted) { - abortController.abort(); - } - - abortController = new AbortController(); - const indicesPromise = httpService.httpClient.get>( `${API_BASE_PATH}/indices_get`, { - signal: abortController.signal, + signal: abortSignal, } ); // Run all requests in parallel - const enrichedPromises = indexDataEnricher.enrichIndices( - httpService.httpClient, - abortController.signal - ); + const enrichedPromises = indexDataEnricher.enrichIndices(httpService.httpClient, abortSignal); // we'll wait for the main request to complete first so the index list has stability const indices = await indicesPromise.catch((error) => { diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js deleted file mode 100644 index 5ce8cdcd4607e..0000000000000 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createAction } from 'redux-actions'; -import { loadIndices as request } from '../../services'; - -export const loadIndicesStart = createAction('INDEX_MANAGEMENT_LOAD_INDICES_START'); -export const loadIndicesSuccess = createAction('INDEX_MANAGEMENT_LOAD_INDICES_SUCCESS'); -export const loadIndicesError = createAction('INDEX_MANAGEMENT_LOAD_INDICES_ERROR'); -export const loadIndicesEnrichmentError = createAction( - 'INDEX_MANAGEMENT_LOAD_INDICES_ENRICHMENT_ERROR' -); - -export const loadIndices = () => async (dispatch) => { - dispatch(loadIndicesStart()); - try { - await request( - (indices) => dispatch(loadIndicesSuccess({ indices })), - (source) => dispatch(loadIndicesEnrichmentError({ source })) - ); - } catch (error) { - return dispatch(loadIndicesError(error)); - } -}; diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index 9429616064563..ec45479003a77 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -20,7 +20,7 @@ export const indexStatsEnricher = { fn: async (client: HttpSetup, signal: AbortSignal): Promise => client .get(`${API_BASE_PATH}/indices_stats`, { signal }) - .then(async (response) => { + .then((response) => { const indices = response.indices || {}; return { indices: Object.keys(indices).map((name) => ({ From 815dd6027e13c6804d3ef2fde1cd3b4f57f1cf98 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 15:11:02 -0600 Subject: [PATCH 39/55] move abort signal up one level --- .../application/store/actions/load_indices.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.ts diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.ts b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.ts new file mode 100644 index 0000000000000..cec4c14cba18e --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.ts @@ -0,0 +1,37 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createAction } from 'redux-actions'; +import { loadIndices as request } from '../../services'; + +export const loadIndicesStart = createAction('INDEX_MANAGEMENT_LOAD_INDICES_START'); +export const loadIndicesSuccess = createAction('INDEX_MANAGEMENT_LOAD_INDICES_SUCCESS'); +export const loadIndicesError = createAction('INDEX_MANAGEMENT_LOAD_INDICES_ERROR'); +export const loadIndicesEnrichmentError = createAction( + 'INDEX_MANAGEMENT_LOAD_INDICES_ENRICHMENT_ERROR' +); + +let abortController: AbortController; + +export const loadIndices = () => async (dispatch: any) => { + if (abortController && !abortController.signal.aborted) { + abortController.abort(); + } + + abortController = new AbortController(); + + dispatch(loadIndicesStart()); + try { + await request( + (indices) => dispatch(loadIndicesSuccess({ indices })), + (source) => dispatch(loadIndicesEnrichmentError({ source })), + abortController.signal + ); + } catch (error) { + return dispatch(loadIndicesError(error)); + } +}; From 3f6400c763834abb6de7577226e1d091bc93c337 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 16:26:05 -0600 Subject: [PATCH 40/55] switch back to js --- .../store/actions/{load_indices.ts => load_indices.js} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename x-pack/platform/plugins/shared/index_management/public/application/store/actions/{load_indices.ts => load_indices.js} (92%) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.ts b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js similarity index 92% rename from x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.ts rename to x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js index cec4c14cba18e..2ebc3bd65f482 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/store/actions/load_indices.js @@ -15,9 +15,9 @@ export const loadIndicesEnrichmentError = createAction( 'INDEX_MANAGEMENT_LOAD_INDICES_ENRICHMENT_ERROR' ); -let abortController: AbortController; +let abortController; -export const loadIndices = () => async (dispatch: any) => { +export const loadIndices = () => async (dispatch) => { if (abortController && !abortController.signal.aborted) { abortController.abort(); } From d57acc788a38bcb209e2b755e4116a629ef3433a Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 21:05:47 -0600 Subject: [PATCH 41/55] fix sorting on size --- .../src/types.ts | 4 +-- .../index_list/index_table/index_table.js | 2 ++ .../public/application/services/sort_table.ts | 28 ------------------- .../public/index_stats_enricher.ts | 7 ++--- 4 files changed, 6 insertions(+), 35 deletions(-) diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts index 3ed7ab84855cf..8224ad7985aed 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/types.ts @@ -145,8 +145,8 @@ export interface IndexAttributes { status?: IndicesStatsIndexMetadataState; uuid?: Uuid; documents?: number; - size?: string; - primary_size?: string; + size?: number; + primary_size?: number; documents_deleted?: number; } diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index 86d7a89439dd8..46d472e7a2748 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { Route } from '@kbn/shared-ux-router'; import qs from 'query-string'; +import numeral from '@elastic/numeral'; import { EuiButton, @@ -135,6 +136,7 @@ const getColumnConfigs = ({ label: i18n.translate('xpack.idxMgmt.indexTable.headers.storageSizeHeader', { defaultMessage: 'Storage size', }), + render: (index) => numeral(index.size).format('0.00 b'), order: 70, } ); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.ts index 1a5463dcba19e..f21557789a3c3 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.ts @@ -20,16 +20,6 @@ type SortField = | 'primary_size' | 'data_stream'; -type Unit = 'kb' | 'mb' | 'gb' | 'tb' | 'pb'; - -const unitMagnitude = { - kb: 1, - mb: 2, - gb: 3, - tb: 4, - pb: 5, -}; - type SortFunction = (index: Index) => any; const numericSort = @@ -37,29 +27,11 @@ const numericSort = (item) => Number(item[fieldName]); -const byteSort = - (fieldName: SortField): SortFunction => - (item) => { - const rawValue = String(item[fieldName]); - // raw value can be missing if index is closed - if (!rawValue) { - return 0; - } - const matchResult = rawValue.match(/(.*)([kmgtp]b)/); - if (!matchResult) { - return 0; - } - const [, number, unit] = matchResult; - return +number * Math.pow(1024, unitMagnitude[unit as Unit]); - }; - const getSorters = (extensionsService?: ExtensionsService) => { const sorters = { primary: numericSort('primary'), replica: numericSort('replica'), documents: numericSort('documents'), - size: byteSort('size'), - primary_size: byteSort('primary_size'), } as any; const columns = extensionsService?.columns ?? []; for (const column of columns) { diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index ec45479003a77..b5c08bff22971 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -9,7 +9,6 @@ import type { HttpSetup } from '@kbn/core/public'; import type { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; import type { EnricherResponse } from '@kbn/index-management-shared-types'; import { i18n } from '@kbn/i18n'; -import numeral from '@elastic/numeral'; import { API_BASE_PATH } from '../common/constants'; const SOURCE = i18n.translate('xpack.idxMgmt.indexStatsEnricher.source', { defaultMessage: 'index stats', @@ -29,11 +28,9 @@ export const indexStatsEnricher = { status: indices[name]?.status, uuid: indices[name]?.uuid, documents_deleted: indices[name]?.primaries?.docs?.deleted ?? 0, - primary_size: numeral(indices[name]?.primaries?.store?.size_in_bytes ?? 0).format( - '0.00 b' - ), + primary_size: indices[name]?.primaries?.store?.size_in_bytes ?? 0, documents: indices[name]?.primaries?.docs?.count ?? 0, - size: numeral(indices[name]?.total?.store?.size_in_bytes ?? 0).format('0.00 b'), + size: indices[name]?.total?.store?.size_in_bytes ?? 0, })), source: SOURCE, }; From 471dc0dc722e4623133cdb2e980c5730133020eb Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 22:17:10 -0600 Subject: [PATCH 42/55] type fix --- .../__jest__/extend_index_management.test.tsx | 2 +- .../common/types/index.ts | 8 +++++ .../add_lifecycle_confirm_modal.tsx | 2 +- .../components/index_lifecycle_summary.tsx | 3 +- .../public/extend_index_management/index.tsx | 3 +- .../index_details_page/mocks.ts | 4 +-- .../application/services/sort_table.test.ts | 36 ------------------- .../server/lib/fetch_indices.test.ts | 4 +-- .../server/lib/fetch_indices.ts | 9 ++--- 9 files changed, 21 insertions(+), 50 deletions(-) diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx index 633b66a53bcb0..e8655cec0fc1c 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx @@ -12,7 +12,7 @@ import { useEuiTheme } from '@elastic/eui'; import { screen, waitFor } from '@testing-library/react'; import { renderWithI18n } from '@kbn/test-jest-helpers'; import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; -import type { Index } from '@kbn/index-management-plugin/common'; +import type { Index } from '../common/types'; import { init } from '../integration_tests/helpers/http_requests'; import { diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts b/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts index 001abc6673242..2c30039585996 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts @@ -9,6 +9,14 @@ export type * from './api'; export * from '@kbn/index-lifecycle-management-common-shared'; +import type { Index as IndexImported } from '@kbn/index-management-plugin/common'; + +// index management plugin uses number for these fields +export interface Index extends Omit { + size?: string; + primary_size?: string; +} + /** * These roles reflect how nodes are stratified into different data tiers. */ diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/add_lifecycle_confirm_modal.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/add_lifecycle_confirm_modal.tsx index 6f4c70b54d491..9aa338c0ddc63 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/add_lifecycle_confirm_modal.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/add_lifecycle_confirm_modal.tsx @@ -26,7 +26,7 @@ import { htmlIdGenerator, } from '@elastic/eui'; -import type { Index } from '@kbn/index-management-plugin/common'; +import type { Index } from '../../../common/types'; import { loadPolicies, addLifecyclePolicyToIndex } from '../../application/services/api'; import { showApiError } from '../../application/services/api_errors'; import { toasts } from '../../application/services/notification'; diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx index 6426e0b2416c5..2e0516517134b 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx @@ -25,8 +25,9 @@ import { } from '@elastic/eui'; import type { ApplicationStart } from '@kbn/core/public'; -import type { Index, IndexDetailsTab } from '@kbn/index-management-shared-types'; +import type { IndexDetailsTab } from '@kbn/index-management-shared-types'; import type { IlmExplainLifecycleLifecycleExplainManaged } from '@elastic/elasticsearch/lib/api/types'; +import type { Index } from '../../../common/types'; import type { Phase } from '../../../common/types'; import { getPolicyEditPath } from '../../application/services/navigation'; import { usePhaseColors } from '../../application/lib'; diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/index.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/index.tsx index cae5b8c5f5842..ecc1049861f3f 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/index.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/index.tsx @@ -11,7 +11,8 @@ import { i18n } from '@kbn/i18n'; import { EuiSearchBar } from '@elastic/eui'; import type { ApplicationStart } from '@kbn/core/public'; -import type { Index, IndexManagementPluginSetup } from '@kbn/index-management-plugin/public'; +import type { IndexManagementPluginSetup } from '@kbn/index-management-plugin/public'; +import type { Index } from '../../common/types'; import { retryLifecycleForIndex } from '../application/services/api'; import { indexLifecycleTab } from './components/index_lifecycle_summary'; diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/mocks.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/mocks.ts index dc24872972cb4..5a6d0b4f93387 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/mocks.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/mocks.ts @@ -17,8 +17,8 @@ export const testIndexMock: Index = { replica: '2', documents: 1, documents_deleted: 0, - size: '20kb', - primary_size: '10kb', + size: 20480, + primary_size: 10240, isFrozen: false, aliases: 'none', hidden: false, diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.test.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.test.ts index 1e5d509a4d130..421a568596ad8 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.test.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/sort_table.test.ts @@ -81,42 +81,6 @@ describe('sortTable', () => { }); }); - describe('sorts by size', () => { - const indices = [{ size: '248b' }, { size: '2.35mb' }, { size: '6.36kb' }] as Index[]; - it('ascending', () => { - const sorted = sortTable(indices, 'size', true); - expect(sorted).toEqual([{ size: '248b' }, { size: '6.36kb' }, { size: '2.35mb' }]); - }); - it('descending', () => { - const sorted = sortTable(indices, 'size', false); - expect(sorted).toEqual([{ size: '2.35mb' }, { size: '6.36kb' }, { size: '248b' }]); - }); - }); - - describe('sorts by primary_size', () => { - const indices = [ - { primary_size: '248b' }, - { primary_size: '2.35mb' }, - { primary_size: '6.36kb' }, - ] as Index[]; - it('ascending', () => { - const sorted = sortTable(indices, 'primary_size', true); - expect(sorted).toEqual([ - { primary_size: '248b' }, - { primary_size: '6.36kb' }, - { primary_size: '2.35mb' }, - ]); - }); - it('descending', () => { - const sorted = sortTable(indices, 'primary_size', false); - expect(sorted).toEqual([ - { primary_size: '2.35mb' }, - { primary_size: '6.36kb' }, - { primary_size: '248b' }, - ]); - }); - }); - describe('sorts by data_stream', () => { const indices = [ { data_stream: 'test1' }, diff --git a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts index 6be9c85058f17..4036dd6aa43cc 100644 --- a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts +++ b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts @@ -160,8 +160,8 @@ describe('[Index management API Routes] fetch indices lib function', () => { health: undefined, status: undefined, documents: 0, - size: '0b', - primary_size: '0b', + size: 0, + primary_size: 0, }), ], }); diff --git a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts index ec8f8541b447d..3581e14ba6d22 100644 --- a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts +++ b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ByteSizeValue } from '@kbn/config-schema'; import type { IScopedClusterClient } from '@kbn/core/server'; import type { IndexDataEnricher } from '../services'; import type { Index } from '..'; @@ -80,10 +79,8 @@ async function fetchIndicesCall( uuid: indexStats?.uuid, documents: indexStats?.primaries?.docs?.count ?? 0, documents_deleted: indexStats?.primaries?.docs?.deleted ?? 0, - size: new ByteSizeValue(indexStats?.total?.store?.size_in_bytes ?? 0).toString(), - primary_size: new ByteSizeValue( - indexStats?.primaries?.store?.size_in_bytes ?? 0 - ).toString(), + size: indexStats?.total?.store?.size_in_bytes ?? 0, + primary_size: indexStats?.primaries?.store?.size_in_bytes ?? 0, }; } @@ -118,7 +115,7 @@ async function fetchIndicesCall( return { ...baseResponse, documents: indexStats?.num_docs ?? 0, - size: new ByteSizeValue(indexStats?.size_in_bytes ?? 0).toString(), + size: indexStats?.size_in_bytes ?? 0, }; } From 4c1b97cc9a91d00e55e622e3c843cc7422ebe232 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Jan 2026 23:22:41 -0600 Subject: [PATCH 43/55] type fixes --- .../__jest__/extend_index_management.test.tsx | 17 ++++++++++------- .../components/index_lifecycle_summary.tsx | 2 +- .../server/routes/api/indices/indices_get.ts | 5 +---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx index e8655cec0fc1c..81b1a7bd2db1b 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx @@ -12,6 +12,7 @@ import { useEuiTheme } from '@elastic/eui'; import { screen, waitFor } from '@testing-library/react'; import { renderWithI18n } from '@kbn/test-jest-helpers'; import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; +import type { Index as IndexManagementIndex } from '@kbn/index-management-shared-types'; import type { Index } from '../common/types'; import { init } from '../integration_tests/helpers/http_requests'; @@ -401,7 +402,7 @@ describe('extend index management', () => { const policyErrorPanel = 'policyErrorPanel'; const phaseDefinitionPanel = 'phaseDefinitionPanel'; - const IlmContentComponent = ({ index }: { index: Index }) => { + const IlmContentComponent = ({ index }: { index: IndexManagementIndex }) => { const { euiTheme } = useEuiTheme(); return ; }; @@ -410,7 +411,7 @@ describe('extend index management', () => { const shouldRenderTab = indexLifecycleTab.shouldRenderTab && indexLifecycleTab.shouldRenderTab({ - index: indexWithoutLifecyclePolicy, + index: indexWithoutLifecyclePolicy as IndexManagementIndex, }); expect(shouldRenderTab).toBeFalsy(); }); @@ -419,11 +420,11 @@ describe('extend index management', () => { const shouldRenderTab = indexLifecycleTab.shouldRenderTab && indexLifecycleTab.shouldRenderTab({ - index: indexWithLifecyclePolicy, + index: indexWithLifecyclePolicy as IndexManagementIndex, }); expect(shouldRenderTab).toBeTruthy(); const { container } = renderWithI18n( - + ); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); @@ -433,7 +434,9 @@ describe('extend index management', () => { }); test('should render an error panel when index has lifecycle error', () => { - const { container } = renderWithI18n(); + const { container } = renderWithI18n( + + ); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); expect(screen.queryByTestId(phaseDefinitionPanel)).not.toBeInTheDocument(); @@ -443,7 +446,7 @@ describe('extend index management', () => { test('should render a phase definition panel when lifecycle has phase definition', () => { const { container } = renderWithI18n( - + ); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); @@ -454,7 +457,7 @@ describe('extend index management', () => { test('should render a step info panel when lifecycle is waiting for a step completion', () => { const { container } = renderWithI18n( - + ); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx index 2e0516517134b..46241a75e9864 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx @@ -33,7 +33,7 @@ import { getPolicyEditPath } from '../../application/services/navigation'; import { usePhaseColors } from '../../application/lib'; interface IndexLifecycleSummaryProps { - index: Index; + index: Omit; getUrlForApp: ApplicationStart['getUrlForApp']; } diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts index 6320218e5d1de..97f06e8de683e 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_get.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { ByteSizeValue } from '@kbn/config-schema'; import type { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; import type { MeteringStatsResponse } from '../../../lib/types'; @@ -91,9 +90,7 @@ export function registerIndicesGet({ router, lib: { handleEsError }, config }: R if (indexStatsMap[indexName]) { prev[indexName].documents = indexStatsMap[indexName].num_docs ?? 0; - prev[indexName].size = new ByteSizeValue( - indexStatsMap[indexName].size_in_bytes ?? 0 - ).toString(); + prev[indexName].size = indexStatsMap[indexName].size_in_bytes ?? 0; } return prev; }, {}); From 37b8d1c1da5daff061fa73429b78b93dddda0c33 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 00:48:33 -0600 Subject: [PATCH 44/55] test fix? --- .../client_integration/helpers/actions/data_stream_actions.ts | 4 ++-- .../__jest__/client_integration/home/indices_tab.test.tsx | 4 ++-- .../shared/index_management/server/lib/fetch_indices.test.ts | 2 +- .../index_management/server/test/helpers/indices_fixtures.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/actions/data_stream_actions.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/actions/data_stream_actions.ts index e4ce12ccec173..9aa7c65bf2fcd 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/actions/data_stream_actions.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/actions/data_stream_actions.ts @@ -399,7 +399,7 @@ export const createNonDataStreamIndex = (name: string) => ({ replica: 1, documents: 10000, documents_deleted: 100, - size: '156kb', - primary_size: '156kb', + size: 159744, + primary_size: 159744, name, }); diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx index a80ca90771aae..ecdbab7bb29bb 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/indices_tab.test.tsx @@ -369,7 +369,7 @@ describe('', () => { expect(screen.getByTestId('indexTableCell-primary')).toHaveTextContent('1'); expect(screen.getByTestId('indexTableCell-replica')).toHaveTextContent('1'); expect(screen.getByTestId('indexTableCell-documents')).toBeInTheDocument(); - expect(screen.getByTestId('indexTableCell-size')).toHaveTextContent('156kb'); + expect(screen.getByTestId('indexTableCell-size')).toHaveTextContent('156.00 KB'); }); test('renders only size and docs count when enableIndexStats is false, enableSizeAndDocCount is true', async () => { @@ -392,7 +392,7 @@ describe('', () => { expect(screen.getByTestId('indexTableCell-name')).toHaveTextContent('test'); // Size and docs should be shown expect(screen.getByTestId('indexTableCell-documents')).toBeInTheDocument(); - expect(screen.getByTestId('indexTableCell-size')).toHaveTextContent('156kb'); + expect(screen.getByTestId('indexTableCell-size')).toHaveTextContent('156.00 KB'); // Health, status, primary, replica should NOT be shown (enableIndexStats is false) expect(screen.queryByTestId('indexTableCell-health')).not.toBeInTheDocument(); expect(screen.queryByTestId('indexTableCell-status')).not.toBeInTheDocument(); diff --git a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts index 4036dd6aa43cc..28bdbda9e1afb 100644 --- a/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts +++ b/x-pack/platform/plugins/shared/index_management/server/lib/fetch_indices.test.ts @@ -198,7 +198,7 @@ describe('[Index management API Routes] fetch indices lib function', () => { hidden: false, data_stream: undefined, documents: 100, - size: '1000b', + size: 1000, }, ], }); diff --git a/x-pack/platform/plugins/shared/index_management/server/test/helpers/indices_fixtures.ts b/x-pack/platform/plugins/shared/index_management/server/test/helpers/indices_fixtures.ts index 824fa30d02e5e..f2bed1b6eed6c 100644 --- a/x-pack/platform/plugins/shared/index_management/server/test/helpers/indices_fixtures.ts +++ b/x-pack/platform/plugins/shared/index_management/server/test/helpers/indices_fixtures.ts @@ -44,8 +44,8 @@ export const createTestIndexResponse = (index?: Partial) => { name: 'test_index', primary: 1, replica: 1, - size: '100b', - primary_size: '100b', + size: 100, + primary_size: 100, status: 'open', uuid: 'test_index', ...index, From f74332727400d88aa36d881e1045fb9ac9c0d322 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 09:25:18 -0600 Subject: [PATCH 45/55] fix formatting --- .../details_page_overview/details_page_overview.tsx | 13 +++++++++++-- .../size_doc_count_details.tsx | 2 +- .../details_page_overview/storage_details.tsx | 4 ++-- .../quick_stats/stateful_storage_stat.tsx | 7 ++++--- .../quick_stats/stateless_document_cout_stat.tsx | 3 ++- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx index ef4467a5a4ef5..054b4d6d043af 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx @@ -30,6 +30,7 @@ import { EisCloudConnectPromoCallout, EisUpdateCallout, } from '@kbn/search-api-panels'; +import numeral from '@elastic/numeral'; import { CLOUD_CONNECT_NAV_ID } from '@kbn/deeplinks-management/constants'; import type { Index } from '../../../../../../../common'; import { useAppContext } from '../../../../../app_context'; @@ -84,6 +85,9 @@ export const DetailsPageOverview: React.FunctionComponent = ({ indexDetai const { data } = useUserPrivileges(indexDetails.name); const hasUpdateMappingsPrivileges = data?.privileges?.canManageIndex === true; + const sizeFormatted = numeral(size).format('0.0b'); + const primarySizeFormatted = numeral(primarySize).format('0.0b'); + const codeSnippetArguments: LanguageDefinitionSnippetArguments = { url: elasticsearchUrl, apiKey: 'your_api_key', @@ -141,7 +145,12 @@ export const DetailsPageOverview: React.FunctionComponent = ({ indexDetai )} - + = ({ indexDetai health={health} /> - + diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx index 456d9096ba9fd..29a7e8919ef79 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx @@ -22,7 +22,7 @@ import { useAppContext } from '../../../../../app_context'; import { OverviewCard } from './overview_card'; export const SizeDocCountDetails: FunctionComponent<{ - size: Index['size']; + size: string; documents: Index['documents']; }> = ({ size, documents }) => { const largeFontSize = useEuiFontSize('l').fontSize; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx index 470d5197938ae..ac4cbdc0dbefe 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx @@ -23,8 +23,8 @@ import type { Index } from '../../../../../../../common'; import { OverviewCard } from './overview_card'; export const StorageDetails: FunctionComponent<{ - primarySize: Index['primary_size']; - size: Index['size']; + primarySize: string; + size: string; primary: Index['primary']; replica: Index['replica']; }> = ({ primarySize, size, primary, replica }) => { diff --git a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx index dea0d05ce8b17..ef9c77398f123 100644 --- a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx +++ b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx @@ -7,6 +7,7 @@ import React from 'react'; import type { Index } from '@kbn/index-management-shared-types'; +import numeral from '@elastic/numeral'; import { useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -36,17 +37,17 @@ export const StatefulIndexStorageStat = ({ defaultMessage: 'Storage', })} data-test-subj="QuickStatsStorage" - secondaryTitle={index.size ?? '0b'} + secondaryTitle={numeral(index.size ?? 0).format('0.00 b')} stats={[ { title: INDEX_SIZE_LABEL, - description: index.size ?? '0b', + description: numeral(index.size ?? 0).format('0.00 b'), }, { title: i18n.translate('xpack.searchIndices.quickStats.primarySize_title', { defaultMessage: 'Primary Size', }), - description: index.primary_size ?? '0b', + description: numeral(index.primary_size ?? 0).format('0.00 b'), }, ]} /> diff --git a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx index 3b6ecee0576a0..8c80c5c0b8c46 100644 --- a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx +++ b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx @@ -7,6 +7,7 @@ import React from 'react'; import type { Index } from '@kbn/index-management-shared-types'; +import numeral from '@elastic/numeral'; import { EuiI18nNumber, useEuiTheme } from '@elastic/eui'; @@ -48,7 +49,7 @@ export const StatelessDocumentCountStat = ({ }, { title: INDEX_SIZE_LABEL, - description: index.size ?? '0b', + description: numeral(index.size ?? 0).format('0.00 b'), }, ]} tooltipContent={DOCUMENT_COUNT_TOOLTIP} From d71c71d679ba8ed04a3600eb0f72a1d760bf3de1 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 11:04:34 -0600 Subject: [PATCH 46/55] functional test fixes --- .../index_details_page/index_details_page.test.tsx | 7 ++++++- .../shared/index_management/public/index_stats_enricher.ts | 1 - .../server/routes/api/indices/indices_stats.ts | 1 - .../test/functional_search/tests/search_index_details.ts | 2 +- .../search/test/page_objects/search_index_details_page.ts | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx index db57e04968500..8c93a7adf3f35 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx @@ -12,6 +12,7 @@ import { MemoryRouter } from '@kbn/shared-ux-router'; import { Route } from '@kbn/shared-ux-router'; import { EuiButtonGroupTestHarness, EuiComboBoxTestHarness } from '@kbn/test-eui-helpers'; import type { RouteComponentProps } from 'react-router-dom'; +import numeral from '@elastic/numeral'; import type { IndexDetailsTab, IndexDetailsTabId } from '../../../common/constants'; import { IndexDetailsSection } from '../../../common/constants'; @@ -373,7 +374,11 @@ describe('', () => { await renderPage(); const storageDetails = screen.getByTestId('indexDetailsStorage').textContent; expect(storageDetails).toBe( - `Storage${testIndexMock.primary_size}Primary${testIndexMock.size}TotalShards${testIndexMock.primary} Primary / ${testIndexMock.replica} Replicas ` + `Storage${numeral(testIndexMock.primary_size).format('0.00 b')}Primary${numeral( + testIndexMock.size + ).format('0.00 b')}TotalShards${testIndexMock.primary} Primary / ${ + testIndexMock.replica + } Replicas ` ); }); diff --git a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts index b5c08bff22971..56ccca366d1d0 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index_stats_enricher.ts @@ -29,7 +29,6 @@ export const indexStatsEnricher = { uuid: indices[name]?.uuid, documents_deleted: indices[name]?.primaries?.docs?.deleted ?? 0, primary_size: indices[name]?.primaries?.store?.size_in_bytes ?? 0, - documents: indices[name]?.primaries?.docs?.count ?? 0, size: indices[name]?.total?.store?.size_in_bytes ?? 0, })), source: SOURCE, diff --git a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts index bbbc70e186333..a028c012583cc 100644 --- a/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts +++ b/x-pack/platform/plugins/shared/index_management/server/routes/api/indices/indices_stats.ts @@ -31,7 +31,6 @@ export function registerIndicesStats({ router, lib: { handleEsError } }: RouteDe '*.*.uuid', '*.*.status', '*.*.health', - '*.*.primaries.docs.count', '*.*.primaries.docs.deleted', '*.*.total.store.size_in_bytes', '*.*.primaries.store.size_in_bytes', diff --git a/x-pack/solutions/search/test/functional_search/tests/search_index_details.ts b/x-pack/solutions/search/test/functional_search/tests/search_index_details.ts index 462c4fd3803a1..4a6f02a6668d5 100644 --- a/x-pack/solutions/search/test/functional_search/tests/search_index_details.ts +++ b/x-pack/solutions/search/test/functional_search/tests/search_index_details.ts @@ -112,7 +112,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('should have quick stats', async () => { await pageObjects.searchIndexDetailsPage.expectQuickStats(); await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveIndexStatus(); - await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveIndexStorage('227b'); + await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveIndexStorage('227.00 B'); await pageObjects.searchIndexDetailsPage.expectQuickStatsAIMappings(); }); diff --git a/x-pack/solutions/search/test/page_objects/search_index_details_page.ts b/x-pack/solutions/search/test/page_objects/search_index_details_page.ts index 4a1cba6b4924d..fa76ba22f4099 100644 --- a/x-pack/solutions/search/test/page_objects/search_index_details_page.ts +++ b/x-pack/solutions/search/test/page_objects/search_index_details_page.ts @@ -74,9 +74,9 @@ export function SearchIndexDetailPageProvider({ getService }: FtrProviderContext 'QuickStatsDocumentCount' ); expect(await quickStatsDocumentElem.getVisibleText()).to.contain('Document count\n0'); - expect(await quickStatsDocumentElem.getVisibleText()).not.to.contain('Index Size\n0b'); + expect(await quickStatsDocumentElem.getVisibleText()).not.to.contain('Index Size\n0.00 B'); await quickStatsDocumentElem.click(); - expect(await quickStatsDocumentElem.getVisibleText()).to.contain('Index Size\n0b'); + expect(await quickStatsDocumentElem.getVisibleText()).to.contain('Index Size\n0.00 B'); }, async expectQuickStatsToHaveIndexStatus() { From fb195a4a7e42ae8b2a0276ebf2c5289c001c5856 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 18:25:33 -0600 Subject: [PATCH 47/55] centralize formatter --- .../rollup/public/rollup_data_enricher.ts | 19 +++++++++++-------- .../index_details_page.test.tsx | 9 +++------ .../shared/index_management/common/index.ts | 2 +- .../index_management/common/lib/index.ts | 2 ++ .../details_page_overview.tsx | 7 +++---- .../index_list/index_table/index_table.js | 4 ++-- .../public/application/services/api.ts | 1 + .../quick_stats/stateful_storage_stat.tsx | 8 ++++---- .../stateless_document_cout_stat.tsx | 4 ++-- 9 files changed, 29 insertions(+), 27 deletions(-) diff --git a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts index af163983bddf4..79fa64fc525de 100644 --- a/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts +++ b/x-pack/platform/plugins/private/rollup/public/rollup_data_enricher.ts @@ -21,15 +21,18 @@ export const rollupDataEnricher = { .then((response) => { return { applyToAliases: true, - indices: Object.keys(response).reduce((acc, rollupJob) => { - response[rollupJob].rollup_jobs.forEach((job) => { - acc.push({ - name: job.rollup_index, - isRollupIndex: true, + indices: Object.keys(response).reduce<{ name: string; isRollupIndex: true }[]>( + (acc, rollupJob) => { + response[rollupJob].rollup_jobs.forEach((job) => { + acc.push({ + name: job.rollup_index, + isRollupIndex: true, + }); }); - }); - return acc; - }, [] as { name: string; isRollupIndex: true }[]), + return acc; + }, + [] + ), source: SOURCE, }; }), diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx index 8c93a7adf3f35..68a486fa7d686 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx @@ -12,11 +12,10 @@ import { MemoryRouter } from '@kbn/shared-ux-router'; import { Route } from '@kbn/shared-ux-router'; import { EuiButtonGroupTestHarness, EuiComboBoxTestHarness } from '@kbn/test-eui-helpers'; import type { RouteComponentProps } from 'react-router-dom'; -import numeral from '@elastic/numeral'; import type { IndexDetailsTab, IndexDetailsTabId } from '../../../common/constants'; import { IndexDetailsSection } from '../../../common/constants'; -import { API_BASE_PATH, INTERNAL_API_BASE_PATH } from '../../../common'; +import { API_BASE_PATH, INTERNAL_API_BASE_PATH, formatBytes } from '../../../common'; import { DetailsPage } from '../../../public/application/sections/home/index_list/details_page/details_page'; import { TYPE_DEFINITION } from '../../../public/application/components/mappings_editor/constants'; @@ -374,11 +373,9 @@ describe('', () => { await renderPage(); const storageDetails = screen.getByTestId('indexDetailsStorage').textContent; expect(storageDetails).toBe( - `Storage${numeral(testIndexMock.primary_size).format('0.00 b')}Primary${numeral( + `Storage${formatBytes(testIndexMock.primary_size)}Primary${formatBytes( testIndexMock.size - ).format('0.00 b')}TotalShards${testIndexMock.primary} Primary / ${ - testIndexMock.replica - } Replicas ` + )}TotalShards${testIndexMock.primary} Primary / ${testIndexMock.replica} Replicas ` ); }); diff --git a/x-pack/platform/plugins/shared/index_management/common/index.ts b/x-pack/platform/plugins/shared/index_management/common/index.ts index d16be3b4ccc05..1373da1dc504c 100644 --- a/x-pack/platform/plugins/shared/index_management/common/index.ts +++ b/x-pack/platform/plugins/shared/index_management/common/index.ts @@ -9,7 +9,7 @@ export { API_BASE_PATH, INTERNAL_API_BASE_PATH, BASE_PATH, MAJOR_VERSION } from './constants'; -export { getTemplateParameter, splitSizeAndUnits } from './lib'; +export { getTemplateParameter, splitSizeAndUnits, formatBytes } from './lib'; export type { Aliases, diff --git a/x-pack/platform/plugins/shared/index_management/common/lib/index.ts b/x-pack/platform/plugins/shared/index_management/common/lib/index.ts index da29f3289bcd4..fea335284fcd7 100644 --- a/x-pack/platform/plugins/shared/index_management/common/lib/index.ts +++ b/x-pack/platform/plugins/shared/index_management/common/lib/index.ts @@ -29,3 +29,5 @@ export { } from './component_template_serialization'; export { getPolicyType, serializeAsESPolicy, getESPolicyCreationApiCall } from './enrich_policies'; + +export { formatBytes } from './format_bytes'; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx index 054b4d6d043af..009beb69767e0 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx @@ -30,9 +30,8 @@ import { EisCloudConnectPromoCallout, EisUpdateCallout, } from '@kbn/search-api-panels'; -import numeral from '@elastic/numeral'; import { CLOUD_CONNECT_NAV_ID } from '@kbn/deeplinks-management/constants'; -import type { Index } from '../../../../../../../common'; +import { type Index, formatBytes } from '../../../../../../../common'; import { useAppContext } from '../../../../../app_context'; import { documentationService, useLoadIndexMappings } from '../../../../../services'; import { languageDefinitions, curlDefinition } from './languages'; @@ -85,8 +84,8 @@ export const DetailsPageOverview: React.FunctionComponent = ({ indexDetai const { data } = useUserPrivileges(indexDetails.name); const hasUpdateMappingsPrivileges = data?.privileges?.canManageIndex === true; - const sizeFormatted = numeral(size).format('0.0b'); - const primarySizeFormatted = numeral(primarySize).format('0.0b'); + const sizeFormatted = formatBytes(size); + const primarySizeFormatted = formatBytes(primarySize); const codeSnippetArguments: LanguageDefinitionSnippetArguments = { url: elasticsearchUrl, diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index 46d472e7a2748..112572b9ac80a 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -10,7 +10,6 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { Route } from '@kbn/shared-ux-router'; import qs from 'query-string'; -import numeral from '@elastic/numeral'; import { EuiButton, @@ -42,6 +41,7 @@ import { reactRouterNavigate, attemptToURIDecode, } from '../../../../../shared_imports'; +import { formatBytes } from '../../../../../../common'; import { getDataStreamDetailsLink, navigateToIndexDetailsPage } from '../../../../services/routing'; import { documentationService } from '../../../../services/documentation'; import { AppContextConsumer } from '../../../../app_context'; @@ -136,7 +136,7 @@ const getColumnConfigs = ({ label: i18n.translate('xpack.idxMgmt.indexTable.headers.storageSizeHeader', { defaultMessage: 'Storage size', }), - render: (index) => numeral(index.size).format('0.00 b'), + render: (index) => formatBytes(index.size), order: 70, } ); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts index 2d8365d184414..09fdc05d7bb36 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/api.ts @@ -162,6 +162,7 @@ export async function loadIndices( // return undefined and exit early if the request was aborted return; } + throw error; }); if (!indices) { diff --git a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx index ef9c77398f123..57d6e1d1356b3 100644 --- a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx +++ b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Index } from '@kbn/index-management-shared-types'; -import numeral from '@elastic/numeral'; +import { formatBytes } from '@kbn/index-management-plugin/common'; import { useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -37,17 +37,17 @@ export const StatefulIndexStorageStat = ({ defaultMessage: 'Storage', })} data-test-subj="QuickStatsStorage" - secondaryTitle={numeral(index.size ?? 0).format('0.00 b')} + secondaryTitle={formatBytes(index.size)} stats={[ { title: INDEX_SIZE_LABEL, - description: numeral(index.size ?? 0).format('0.00 b'), + description: formatBytes(index.size), }, { title: i18n.translate('xpack.searchIndices.quickStats.primarySize_title', { defaultMessage: 'Primary Size', }), - description: numeral(index.primary_size ?? 0).format('0.00 b'), + description: formatBytes(index.primary_size), }, ]} /> diff --git a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx index 8c80c5c0b8c46..b19029371f807 100644 --- a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx +++ b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Index } from '@kbn/index-management-shared-types'; -import numeral from '@elastic/numeral'; +import { formatBytes } from '@kbn/index-management-plugin/common'; import { EuiI18nNumber, useEuiTheme } from '@elastic/eui'; @@ -49,7 +49,7 @@ export const StatelessDocumentCountStat = ({ }, { title: INDEX_SIZE_LABEL, - description: numeral(index.size ?? 0).format('0.00 b'), + description: formatBytes(index.size), }, ]} tooltipContent={DOCUMENT_COUNT_TOOLTIP} From 4dc24cb421c59369e2e9ae118181cb6e010e4564 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 30 Jan 2026 00:36:25 +0000 Subject: [PATCH 48/55] Changes from node scripts/lint_ts_projects --fix --- x-pack/solutions/search/plugins/search_indices/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/solutions/search/plugins/search_indices/tsconfig.json b/x-pack/solutions/search/plugins/search_indices/tsconfig.json index f254a41ee0b33..00ebee4130e9a 100644 --- a/x-pack/solutions/search/plugins/search_indices/tsconfig.json +++ b/x-pack/solutions/search/plugins/search_indices/tsconfig.json @@ -48,6 +48,7 @@ "@kbn/react-query", "@kbn/search-api-panels", "@kbn/deeplinks-management", + "@kbn/index-management-plugin", ], "exclude": [ "target/**/*", From 939b61bc2d7ecee62b613f8eea8c30cd291c1b89 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 30 Jan 2026 00:48:05 +0000 Subject: [PATCH 49/55] Changes from node scripts/regenerate_moon_projects.js --update --- x-pack/solutions/search/plugins/search_indices/moon.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/solutions/search/plugins/search_indices/moon.yml b/x-pack/solutions/search/plugins/search_indices/moon.yml index f0507ee234df6..bc4df7abed64a 100644 --- a/x-pack/solutions/search/plugins/search_indices/moon.yml +++ b/x-pack/solutions/search/plugins/search_indices/moon.yml @@ -55,6 +55,7 @@ dependsOn: - '@kbn/react-query' - '@kbn/search-api-panels' - '@kbn/deeplinks-management' + - '@kbn/index-management-plugin' tags: - plugin - prod From dec6095a47cc75af0ed07825fd810afb67e40a9c Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 19:02:01 -0600 Subject: [PATCH 50/55] centralize formatter --- .../shared/index_management/common/lib/format_bytes.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts diff --git a/x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts b/x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts new file mode 100644 index 0000000000000..c673f77c84595 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts @@ -0,0 +1,10 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import numeral from '@elastic/numeral'; + +export const formatBytes = (bytes?: number): string => numeral(bytes || 0).format('0.00 b'); From 6e8b0997bcb8e3531aafa050937e720ee3c318b6 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 20:16:51 -0600 Subject: [PATCH 51/55] import fix --- .../index_details_page/index_details_page.test.tsx | 3 ++- .../plugins/shared/index_management/common/index.ts | 2 +- .../shared/index_management/common/lib/format_bytes.ts | 10 ---------- .../shared/index_management/common/lib/index.ts | 2 -- .../details_page_overview/details_page_overview.tsx | 3 ++- .../home/index_list/index_table/index_table.js | 2 +- .../plugins/shared/index_management/public/index.ts | 2 ++ .../components/quick_stats/stateful_storage_stat.tsx | 2 +- .../quick_stats/stateless_document_cout_stat.tsx | 2 +- 9 files changed, 10 insertions(+), 18 deletions(-) delete mode 100644 x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx index 68a486fa7d686..139a0d3bef5d1 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx @@ -15,7 +15,8 @@ import type { RouteComponentProps } from 'react-router-dom'; import type { IndexDetailsTab, IndexDetailsTabId } from '../../../common/constants'; import { IndexDetailsSection } from '../../../common/constants'; -import { API_BASE_PATH, INTERNAL_API_BASE_PATH, formatBytes } from '../../../common'; +import { API_BASE_PATH, INTERNAL_API_BASE_PATH } from '../../../common'; +import { formatBytes } from '../../../public'; import { DetailsPage } from '../../../public/application/sections/home/index_list/details_page/details_page'; import { TYPE_DEFINITION } from '../../../public/application/components/mappings_editor/constants'; diff --git a/x-pack/platform/plugins/shared/index_management/common/index.ts b/x-pack/platform/plugins/shared/index_management/common/index.ts index 1373da1dc504c..d16be3b4ccc05 100644 --- a/x-pack/platform/plugins/shared/index_management/common/index.ts +++ b/x-pack/platform/plugins/shared/index_management/common/index.ts @@ -9,7 +9,7 @@ export { API_BASE_PATH, INTERNAL_API_BASE_PATH, BASE_PATH, MAJOR_VERSION } from './constants'; -export { getTemplateParameter, splitSizeAndUnits, formatBytes } from './lib'; +export { getTemplateParameter, splitSizeAndUnits } from './lib'; export type { Aliases, diff --git a/x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts b/x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts deleted file mode 100644 index c673f77c84595..0000000000000 --- a/x-pack/platform/plugins/shared/index_management/common/lib/format_bytes.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import numeral from '@elastic/numeral'; - -export const formatBytes = (bytes?: number): string => numeral(bytes || 0).format('0.00 b'); diff --git a/x-pack/platform/plugins/shared/index_management/common/lib/index.ts b/x-pack/platform/plugins/shared/index_management/common/lib/index.ts index fea335284fcd7..da29f3289bcd4 100644 --- a/x-pack/platform/plugins/shared/index_management/common/lib/index.ts +++ b/x-pack/platform/plugins/shared/index_management/common/lib/index.ts @@ -29,5 +29,3 @@ export { } from './component_template_serialization'; export { getPolicyType, serializeAsESPolicy, getESPolicyCreationApiCall } from './enrich_policies'; - -export { formatBytes } from './format_bytes'; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx index 009beb69767e0..429d1d2b7823b 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/details_page_overview.tsx @@ -31,7 +31,8 @@ import { EisUpdateCallout, } from '@kbn/search-api-panels'; import { CLOUD_CONNECT_NAV_ID } from '@kbn/deeplinks-management/constants'; -import { type Index, formatBytes } from '../../../../../../../common'; +import { type Index } from '../../../../../../../common'; +import { formatBytes } from '../../../../../lib/format_bytes'; import { useAppContext } from '../../../../../app_context'; import { documentationService, useLoadIndexMappings } from '../../../../../services'; import { languageDefinitions, curlDefinition } from './languages'; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js index 112572b9ac80a..c2801ee187585 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -41,7 +41,7 @@ import { reactRouterNavigate, attemptToURIDecode, } from '../../../../../shared_imports'; -import { formatBytes } from '../../../../../../common'; +import { formatBytes } from '../../../../..'; import { getDataStreamDetailsLink, navigateToIndexDetailsPage } from '../../../../services/routing'; import { documentationService } from '../../../../services/documentation'; import { AppContextConsumer } from '../../../../app_context'; diff --git a/x-pack/platform/plugins/shared/index_management/public/index.ts b/x-pack/platform/plugins/shared/index_management/public/index.ts index 36d2cdefa9efb..e6c7982e4019f 100644 --- a/x-pack/platform/plugins/shared/index_management/public/index.ts +++ b/x-pack/platform/plugins/shared/index_management/public/index.ts @@ -24,3 +24,5 @@ export { getIndexListUri, getTemplateDetailsLink } from './application/services/ export type { IndexManagementLocatorParams } from '@kbn/index-management-shared-types'; export { INDEX_MANAGEMENT_LOCATOR_ID } from './locator'; + +export { formatBytes } from './application/lib/format_bytes'; diff --git a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx index 57d6e1d1356b3..294e058844bbb 100644 --- a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx +++ b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateful_storage_stat.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Index } from '@kbn/index-management-shared-types'; -import { formatBytes } from '@kbn/index-management-plugin/common'; +import { formatBytes } from '@kbn/index-management-plugin/public'; import { useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx index b19029371f807..e23bae5e437f8 100644 --- a/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx +++ b/x-pack/solutions/search/plugins/search_indices/public/components/quick_stats/stateless_document_cout_stat.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Index } from '@kbn/index-management-shared-types'; -import { formatBytes } from '@kbn/index-management-plugin/common'; +import { formatBytes } from '@kbn/index-management-plugin/public'; import { EuiI18nNumber, useEuiTheme } from '@elastic/eui'; From db62978ec6f58910f3f6f56f2de75cf42eab0343 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 20:17:29 -0600 Subject: [PATCH 52/55] import fix --- .../public/application/lib/format_bytes.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 x-pack/platform/plugins/shared/index_management/public/application/lib/format_bytes.ts diff --git a/x-pack/platform/plugins/shared/index_management/public/application/lib/format_bytes.ts b/x-pack/platform/plugins/shared/index_management/public/application/lib/format_bytes.ts new file mode 100644 index 0000000000000..c673f77c84595 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/public/application/lib/format_bytes.ts @@ -0,0 +1,10 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import numeral from '@elastic/numeral'; + +export const formatBytes = (bytes?: number): string => numeral(bytes || 0).format('0.00 b'); From 14300c845ab8672e44a1e25151d1fb2d2311297b Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 29 Jan 2026 23:03:16 -0600 Subject: [PATCH 53/55] type changes --- .../__jest__/extend_index_management.test.tsx | 41 +++++++++---------- .../common/types/index.ts | 8 +--- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx index 81b1a7bd2db1b..cdf1101043383 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx @@ -12,7 +12,6 @@ import { useEuiTheme } from '@elastic/eui'; import { screen, waitFor } from '@testing-library/react'; import { renderWithI18n } from '@kbn/test-jest-helpers'; import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; -import type { Index as IndexManagementIndex } from '@kbn/index-management-shared-types'; import type { Index } from '../common/types'; import { init } from '../integration_tests/helpers/http_requests'; @@ -67,8 +66,8 @@ const indexWithoutLifecyclePolicy: Index = { replica: 1, documents: 1, documents_deleted: 0, - size: '3.4kb', - primary_size: '3.4kb', + size: 3480, + primary_size: 3480, aliases: 'none', isFrozen: false, hidden: false, @@ -87,8 +86,8 @@ const indexWithLifecyclePolicy: Index = { replica: 1, documents: 2, documents_deleted: 0, - size: '6.5kb', - primary_size: '6.5kb', + size: 6656, + primary_size: 6656, aliases: 'none', isFrozen: false, hidden: false, @@ -116,8 +115,8 @@ const indexWithLifecycleError: Index = { replica: 1, documents: 2, documents_deleted: 0, - size: '6.5kb', - primary_size: '6.5kb', + size: 6656, + primary_size: 6656, aliases: 'none', isFrozen: false, hidden: false, @@ -149,8 +148,8 @@ const indexWithLifecyclePhaseDefinition: Index = { replica: 1, documents: 2, documents_deleted: 0, - size: '6.5kb', - primary_size: '6.5kb', + size: 6656, + primary_size: 6656, aliases: 'none', isFrozen: false, hidden: false, @@ -183,8 +182,8 @@ const indexWithLifecycleWaitingStep: Index = { replica: 1, documents: 2, documents_deleted: 0, - size: '6.5kb', - primary_size: '6.5kb', + size: 6656, + primary_size: 6656, aliases: 'none', isFrozen: false, hidden: false, @@ -217,8 +216,8 @@ const indexWithNonExistentPolicyError: Index = { replica: 1, documents: 2, documents_deleted: 0, - size: '6.5kb', - primary_size: '6.5kb', + size: 6656, + primary_size: 6656, aliases: 'none', isFrozen: false, hidden: false, @@ -402,7 +401,7 @@ describe('extend index management', () => { const policyErrorPanel = 'policyErrorPanel'; const phaseDefinitionPanel = 'phaseDefinitionPanel'; - const IlmContentComponent = ({ index }: { index: IndexManagementIndex }) => { + const IlmContentComponent = ({ index }: { index: Index }) => { const { euiTheme } = useEuiTheme(); return ; }; @@ -411,7 +410,7 @@ describe('extend index management', () => { const shouldRenderTab = indexLifecycleTab.shouldRenderTab && indexLifecycleTab.shouldRenderTab({ - index: indexWithoutLifecyclePolicy as IndexManagementIndex, + index: indexWithoutLifecyclePolicy as Index, }); expect(shouldRenderTab).toBeFalsy(); }); @@ -420,11 +419,11 @@ describe('extend index management', () => { const shouldRenderTab = indexLifecycleTab.shouldRenderTab && indexLifecycleTab.shouldRenderTab({ - index: indexWithLifecyclePolicy as IndexManagementIndex, + index: indexWithLifecyclePolicy, }); expect(shouldRenderTab).toBeTruthy(); const { container } = renderWithI18n( - + ); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); @@ -434,9 +433,7 @@ describe('extend index management', () => { }); test('should render an error panel when index has lifecycle error', () => { - const { container } = renderWithI18n( - - ); + const { container } = renderWithI18n(); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); expect(screen.queryByTestId(phaseDefinitionPanel)).not.toBeInTheDocument(); @@ -446,7 +443,7 @@ describe('extend index management', () => { test('should render a phase definition panel when lifecycle has phase definition', () => { const { container } = renderWithI18n( - + ); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); @@ -457,7 +454,7 @@ describe('extend index management', () => { test('should render a step info panel when lifecycle is waiting for a step completion', () => { const { container } = renderWithI18n( - + ); expect(container).toMatchSnapshot(); expect(screen.getByTestId(policyPropertiesPanel)).toBeInTheDocument(); diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts b/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts index 2c30039585996..bee31c373d69a 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts +++ b/x-pack/platform/plugins/private/index_lifecycle_management/common/types/index.ts @@ -9,13 +9,7 @@ export type * from './api'; export * from '@kbn/index-lifecycle-management-common-shared'; -import type { Index as IndexImported } from '@kbn/index-management-plugin/common'; - -// index management plugin uses number for these fields -export interface Index extends Omit { - size?: string; - primary_size?: string; -} +export type { Index } from '@kbn/index-management-plugin/common'; /** * These roles reflect how nodes are stratified into different data tiers. From a4752e7ab66b4435ddba97bed120835b88c5a634 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 30 Jan 2026 00:04:53 -0600 Subject: [PATCH 54/55] minor cleanup --- .../__jest__/extend_index_management.test.tsx | 2 +- .../components/index_lifecycle_summary.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx index cdf1101043383..932f35198b7d8 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/__jest__/extend_index_management.test.tsx @@ -410,7 +410,7 @@ describe('extend index management', () => { const shouldRenderTab = indexLifecycleTab.shouldRenderTab && indexLifecycleTab.shouldRenderTab({ - index: indexWithoutLifecyclePolicy as Index, + index: indexWithoutLifecyclePolicy, }); expect(shouldRenderTab).toBeFalsy(); }); diff --git a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx index 46241a75e9864..2e0516517134b 100644 --- a/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx +++ b/x-pack/platform/plugins/private/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx @@ -33,7 +33,7 @@ import { getPolicyEditPath } from '../../application/services/navigation'; import { usePhaseColors } from '../../application/lib'; interface IndexLifecycleSummaryProps { - index: Omit; + index: Index; getUrlForApp: ApplicationStart['getUrlForApp']; } From 0327c36c7e5542d08bebf7f3b0f9003c960e082e Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 30 Jan 2026 11:13:20 -0600 Subject: [PATCH 55/55] format document count --- .../details_page_overview/size_doc_count_details.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx index 29a7e8919ef79..9ffc2b432bbb4 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx @@ -17,6 +17,7 @@ import { useEuiFontSize, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { EuiI18nNumber } from '@elastic/eui'; import type { Index } from '../../../../../../../common'; import { useAppContext } from '../../../../../app_context'; import { OverviewCard } from './overview_card'; @@ -65,7 +66,9 @@ export const SizeDocCountDetails: FunctionComponent<{ - {documents} + + + {i18n.translate(