diff --git a/x-pack/plugins/index_management/README.md b/x-pack/plugins/index_management/README.md index 8673447fc577c..867ccbd74335c 100644 --- a/x-pack/plugins/index_management/README.md +++ b/x-pack/plugins/index_management/README.md @@ -34,14 +34,15 @@ interface IndexDetailsTab { An example of adding an ILM tab can be found in [this file](https://github.com/elastic/kibana/blob/main/x-pack/plugins/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx#L250). -- `setIndexOverviewContent(content: IndexOverviewContent)`: replaces the default content in the overview tab (code block describing adding documents to the index) with the custom content. The custom content has the following interface: +- `setIndexOverviewContent(content: IndexContent)`: replaces the default content in the overview tab (code block describing adding documents to the index) with the custom content. The custom content has the following interface: ```ts -interface IndexOverviewContent { +interface IndexContent { renderContent: (args: { index: Index; getUrlForApp: ApplicationStart['getUrlForApp']; }) => ReturnType; ``` +- `setIndexMappingsContent(content: IndexContent)`: adds content to the mappings tab of the index details page. The content is displayed in the right bottom corner, below the mappings docs link. ## Indices tab diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx index afdfa15c917aa..24938682ac519 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx @@ -501,6 +501,28 @@ describe('', () => { expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); }); }); + + it('renders the content set via the extensions service', async () => { + const mappingsContent = 'test mappings extension'; + await act(async () => { + testBed = await setup({ + httpSetup, + dependencies: { + services: { + extensionsService: { + _indexMappingsContent: { + renderContent: () => mappingsContent, + }, + }, + }, + }, + }); + }); + testBed.component.update(); + await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Mappings); + const content = testBed.actions.getActiveTabContent(); + expect(content).toContain(mappingsContent); + }); }); describe('Settings tab', () => { diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_content.tsx index 0360df733c945..adeadee6132c0 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_content.tsx @@ -50,7 +50,7 @@ const defaultTabs: IndexDetailsTab[] = [ name: ( ), - renderTabContent: ({ index }) => , + renderTabContent: ({ index }) => , order: 20, }, { diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx index a83eebd395b1e..c6f74207c92d7 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx @@ -6,27 +6,18 @@ */ import React, { FunctionComponent, useEffect } from 'react'; -import { - EuiButton, - EuiCodeBlock, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiLink, - EuiPageTemplate, - EuiPanel, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import { css } from '@emotion/react'; +import { EuiButton, EuiPageTemplate, EuiSpacer, EuiText } from '@elastic/eui'; + import { FormattedMessage } from '@kbn/i18n-react'; import { SectionLoading } from '@kbn/es-ui-shared-plugin/public'; -import { useLoadIndexMappings, documentationService } from '../../../../services'; + +import { DetailsPageMappingsContent } from './details_page_mappings_content'; +import { Index } from '../../../../../../common'; +import { useLoadIndexMappings } from '../../../../services'; import { breadcrumbService, IndexManagementBreadcrumb } from '../../../../services/breadcrumbs'; -export const DetailsPageMappings: FunctionComponent<{ indexName: string }> = ({ indexName }) => { - const { isLoading, data, error, resendRequest } = useLoadIndexMappings(indexName); +export const DetailsPageMappings: FunctionComponent<{ index: Index }> = ({ index }) => { + const { isLoading, data, error, resendRequest } = useLoadIndexMappings(index.name); useEffect(() => { breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.indexDetailsMappings); @@ -63,7 +54,7 @@ export const DetailsPageMappings: FunctionComponent<{ indexName: string }> = ({ id="xpack.idxMgmt.indexDetails.mappings.errorDescription" defaultMessage="We encountered an error loading mappings for index {indexName}. Make sure that the index name in the URL is correct and try again." values={{ - indexName, + indexName: index.name, }} /> @@ -86,82 +77,5 @@ export const DetailsPageMappings: FunctionComponent<{ indexName: string }> = ({ ); } - return ( - // using "rowReverse" to keep docs links on the top of the mappings code block on smaller screen - - - - - - - - - -

- -

-
-
-
- - -

- -

-
- - - - -
-
- - - - - {JSON.stringify(data, null, 2)} - - - -
- ); + return ; }; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx new file mode 100644 index 0000000000000..40544bb18e1ff --- /dev/null +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx @@ -0,0 +1,119 @@ +/* + * 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 React, { FunctionComponent } from 'react'; +import { + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; + +import { Index } from '../../../../../../common'; +import { documentationService } from '../../../../services'; +import { useAppContext } from '../../../../app_context'; + +export const DetailsPageMappingsContent: FunctionComponent<{ index: Index; data: any }> = ({ + index, + data, +}) => { + const { + services: { extensionsService }, + core: { getUrlForApp }, + } = useAppContext(); + return ( + // using "rowReverse" to keep docs links on the top of the mappings code block on smaller screen + + + + + + + + + +

+ +

+
+
+
+ + +

+ +

+
+ + + + +
+ {extensionsService.indexMappingsContent && ( + <> + + {extensionsService.indexMappingsContent.renderContent({ index, getUrlForApp })} + + )} +
+ + + + + {JSON.stringify(data, null, 2)} + + + +
+ ); +}; diff --git a/x-pack/plugins/index_management/public/index.ts b/x-pack/plugins/index_management/public/index.ts index b8cde1fdf36e7..8fb836ba7ffd9 100644 --- a/x-pack/plugins/index_management/public/index.ts +++ b/x-pack/plugins/index_management/public/index.ts @@ -14,7 +14,7 @@ export const plugin = (ctx: PluginInitializerContext) => { return new IndexMgmtUIPlugin(ctx); }; -export type { IndexManagementPluginSetup } from './types'; +export type { IndexManagementPluginSetup, IndexManagementPluginStart } from './types'; export { getIndexListUri, getTemplateDetailsLink } from './application/services/routing'; diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 87b4ff5be220c..f4038a4e2677f 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -81,6 +81,10 @@ export class IndexMgmtUIPlugin { }; } - public start() {} + public start() { + return { + extensionsService: this.extensionsService.setup(), + }; + } public stop() {} } diff --git a/x-pack/plugins/index_management/public/services/extensions_service.mock.ts b/x-pack/plugins/index_management/public/services/extensions_service.mock.ts index 3c0886b0fe4a3..8f4968ad35e41 100644 --- a/x-pack/plugins/index_management/public/services/extensions_service.mock.ts +++ b/x-pack/plugins/index_management/public/services/extensions_service.mock.ts @@ -18,6 +18,7 @@ const createServiceMock = (): ExtensionsSetupMock => ({ addToggle: jest.fn(), addIndexDetailsTab: jest.fn(), setIndexOverviewContent: jest.fn(), + setIndexMappingsContent: jest.fn(), }); const createMock = () => { diff --git a/x-pack/plugins/index_management/public/services/extensions_service.ts b/x-pack/plugins/index_management/public/services/extensions_service.ts index 94211b32dbd70..1eb68e9a0b746 100644 --- a/x-pack/plugins/index_management/public/services/extensions_service.ts +++ b/x-pack/plugins/index_management/public/services/extensions_service.ts @@ -12,7 +12,7 @@ import { EuiBadgeProps } from '@elastic/eui'; import type { IndexDetailsTab } from '../../common/constants'; import { Index } from '..'; -export interface IndexOverviewContent { +export interface IndexContent { renderContent: (args: { index: Index; getUrlForApp: ApplicationStart['getUrlForApp']; @@ -28,13 +28,22 @@ export interface IndexBadge { } export interface ExtensionsSetup { + // adds an option to the "manage index" menu addAction(action: any): void; + // adds a banner to the indices list addBanner(banner: any): void; + // adds a filter to the indices list addFilter(filter: any): void; + // adds a badge to the index name addBadge(badge: IndexBadge): void; + // adds a toggle to the indices list addToggle(toggle: any): void; + // adds a tab to the index details page addIndexDetailsTab(tab: IndexDetailsTab): void; - setIndexOverviewContent(content: IndexOverviewContent): void; + // sets content to render instead of the code block on the overview tab of the index page + setIndexOverviewContent(content: IndexContent): void; + // sets content to render below the docs link on the mappings tab of the index page + setIndexMappingsContent(content: IndexContent): void; } export class ExtensionsService { @@ -55,7 +64,8 @@ export class ExtensionsService { ]; private _toggles: any[] = []; private _indexDetailsTabs: IndexDetailsTab[] = []; - private _indexOverviewContent: IndexOverviewContent | null = null; + private _indexOverviewContent: IndexContent | null = null; + private _indexMappingsContent: IndexContent | null = null; private service?: ExtensionsSetup; public setup(): ExtensionsSetup { @@ -66,7 +76,8 @@ export class ExtensionsService { addFilter: this.addFilter.bind(this), addToggle: this.addToggle.bind(this), addIndexDetailsTab: this.addIndexDetailsTab.bind(this), - setIndexOverviewContent: this.setIndexOverviewMainContent.bind(this), + setIndexOverviewContent: this.setIndexOverviewContent.bind(this), + setIndexMappingsContent: this.setIndexMappingsContent.bind(this), }; return this.service; @@ -96,7 +107,7 @@ export class ExtensionsService { this._indexDetailsTabs.push(tab); } - private setIndexOverviewMainContent(content: IndexOverviewContent) { + private setIndexOverviewContent(content: IndexContent) { if (this._indexOverviewContent) { throw new Error(`The content for index overview has already been set.`); } else { @@ -104,6 +115,14 @@ export class ExtensionsService { } } + private setIndexMappingsContent(content: IndexContent) { + if (this._indexMappingsContent) { + throw new Error(`The content for index mappings has already been set.`); + } else { + this._indexMappingsContent = content; + } + } + public get actions() { return this._actions; } @@ -131,4 +150,8 @@ export class ExtensionsService { public get indexOverviewContent() { return this._indexOverviewContent; } + + public get indexMappingsContent() { + return this._indexMappingsContent; + } } diff --git a/x-pack/plugins/index_management/public/services/index.ts b/x-pack/plugins/index_management/public/services/index.ts index 8f4ddbeffba35..bca35e09c9776 100644 --- a/x-pack/plugins/index_management/public/services/index.ts +++ b/x-pack/plugins/index_management/public/services/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export type { ExtensionsSetup } from './extensions_service'; +export type { ExtensionsSetup, IndexContent } from './extensions_service'; export { ExtensionsService } from './extensions_service'; export type { PublicApiServiceSetup } from './public_api_service'; diff --git a/x-pack/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts index 57ddf10c767fd..d00e6ba4d5ac7 100644 --- a/x-pack/plugins/index_management/public/types.ts +++ b/x-pack/plugins/index_management/public/types.ts @@ -16,6 +16,10 @@ export interface IndexManagementPluginSetup { extensionsService: ExtensionsSetup; } +export interface IndexManagementPluginStart { + extensionsService: ExtensionsSetup; +} + export interface SetupDependencies { fleet?: unknown; usageCollection: UsageCollectionSetup; diff --git a/x-pack/plugins/serverless_search/kibana.jsonc b/x-pack/plugins/serverless_search/kibana.jsonc index 2ae8f0dbff987..0ac92bc197468 100644 --- a/x-pack/plugins/serverless_search/kibana.jsonc +++ b/x-pack/plugins/serverless_search/kibana.jsonc @@ -25,7 +25,9 @@ "share", "visualizations" ], - "optionalPlugins": [], + "optionalPlugins": [ + "indexManagement", + ], "requiredBundles": [ "kibanaReact" ] diff --git a/x-pack/plugins/serverless_search/public/application/components/index_mappings_docs_link.tsx b/x-pack/plugins/serverless_search/public/application/components/index_mappings_docs_link.tsx new file mode 100644 index 0000000000000..fc1699beffa8f --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/index_mappings_docs_link.tsx @@ -0,0 +1,68 @@ +/* + * 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 React, { FunctionComponent } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { CoreStart } from '@kbn/core/public'; +import { IndexContent } from '@kbn/index-management-plugin/public/services'; + +const IndexMappingsDocsLink: FunctionComponent<{ docLinks: CoreStart['docLinks'] }> = ({ + docLinks, +}) => { + return ( + + + + + + + +

+ +

+
+
+
+ + +

+ +

+
+ + + + +
+ ); +}; + +export const createIndexMappingsDocsLinkContent = (core: CoreStart): IndexContent => { + return { + renderContent: () => , + }; +}; diff --git a/x-pack/plugins/serverless_search/public/plugin.ts b/x-pack/plugins/serverless_search/public/plugin.ts index 6f1cb6465106c..4203925650385 100644 --- a/x-pack/plugins/serverless_search/public/plugin.ts +++ b/x-pack/plugins/serverless_search/public/plugin.ts @@ -15,6 +15,7 @@ import { import { i18n } from '@kbn/i18n'; import { appIds } from '@kbn/management-cards-navigation'; import { AuthenticatedUser } from '@kbn/security-plugin/common'; +import { createIndexMappingsDocsLinkContent as createIndexMappingsContent } from './application/components/index_mappings_docs_link'; import { createServerlessSearchSideNavComponent as createComponent } from './layout/nav'; import { docLinks } from '../common/doc_links'; import { @@ -85,7 +86,7 @@ export class ServerlessSearchPlugin public start( core: CoreStart, - { serverless, management, cloud }: ServerlessSearchPluginStartDependencies + { serverless, management, cloud, indexManagement }: ServerlessSearchPluginStartDependencies ): ServerlessSearchPluginStart { serverless.setProjectHome('/app/elasticsearch'); serverless.setSideNavComponent(createComponent(core, { serverless, cloud })); @@ -94,6 +95,7 @@ export class ServerlessSearchPlugin enabled: true, hideLinksTo: [appIds.MAINTENANCE_WINDOWS], }); + indexManagement?.extensionsService.setIndexMappingsContent(createIndexMappingsContent(core)); return {}; } diff --git a/x-pack/plugins/serverless_search/public/types.ts b/x-pack/plugins/serverless_search/public/types.ts index 5b984289e2bd7..039353fa4e867 100644 --- a/x-pack/plugins/serverless_search/public/types.ts +++ b/x-pack/plugins/serverless_search/public/types.ts @@ -10,6 +10,7 @@ import { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public' import { SecurityPluginStart } from '@kbn/security-plugin/public'; import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; +import { IndexManagementPluginStart } from '@kbn/index-management-plugin/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessSearchPluginSetup {} @@ -29,4 +30,5 @@ export interface ServerlessSearchPluginStartDependencies { security: SecurityPluginStart; serverless: ServerlessPluginStart; share: SharePluginStart; + indexManagement?: IndexManagementPluginStart; } diff --git a/x-pack/plugins/serverless_search/tsconfig.json b/x-pack/plugins/serverless_search/tsconfig.json index 1886f0ccb21b9..c07cff77aa19e 100644 --- a/x-pack/plugins/serverless_search/tsconfig.json +++ b/x-pack/plugins/serverless_search/tsconfig.json @@ -36,5 +36,6 @@ "@kbn/search-connectors", "@kbn/shared-ux-router", "@kbn/kibana-utils-plugin", + "@kbn/index-management-plugin", ] }