diff --git a/.buildkite/ftr_search_serverless_configs.yml b/.buildkite/ftr_search_serverless_configs.yml index 68dcd0afa06db..d7f4e2620c289 100644 --- a/.buildkite/ftr_search_serverless_configs.yml +++ b/.buildkite/ftr_search_serverless_configs.yml @@ -5,7 +5,6 @@ disabled: defaultQueue: 'n2-4-spot' enabled: - x-pack/solutions/search/test/serverless/api_integration/configs/config.ts - - x-pack/solutions/search/test/serverless/api_integration/configs/config.feature_flags.ts - x-pack/solutions/search/test/serverless/functional/configs/config.ts - x-pack/solutions/search/test/serverless/functional/configs/config.search_features.ts - x-pack/solutions/search/test/serverless/functional/configs/config.feature_flags.ts diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 70320a62a6b02..c5b3ffc9b7c2f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -156,7 +156,7 @@ pageLoadAssetSize: searchHomepage: 9005 searchIndices: 9991 searchInferenceEndpoints: 9400 - searchNavigation: 8308 + searchNavigation: 8900 searchNotebooks: 18826 searchPlayground: 12122 searchprofiler: 6509 diff --git a/src/platform/plugins/private/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/platform/plugins/private/kibana_usage_collection/server/collectors/application_usage/schema.ts index ea64a4fe2640b..662f295580127 100644 --- a/src/platform/plugins/private/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/platform/plugins/private/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -142,7 +142,6 @@ export const applicationUsageSchema = { searchSynonyms: commonSchema, searchQueryRules: commonSchema, elasticConsole: commonSchema, - elasticsearchIndices: commonSchema, elasticsearchIndexManagement: commonSchema, enterpriseSearchAnalytics: commonSchema, enterpriseSearchApplications: commonSchema, diff --git a/src/platform/plugins/shared/telemetry/schema/oss_platform.json b/src/platform/plugins/shared/telemetry/schema/oss_platform.json index 8c3d736b95840..ff43091de0806 100644 --- a/src/platform/plugins/shared/telemetry/schema/oss_platform.json +++ b/src/platform/plugins/shared/telemetry/schema/oss_platform.json @@ -2884,137 +2884,6 @@ } } }, - "elasticsearchIndices": { - "properties": { - "appId": { - "type": "keyword", - "_meta": { - "description": "The application being tracked" - } - }, - "viewId": { - "type": "keyword", - "_meta": { - "description": "Always `main`" - } - }, - "clicks_total": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application since we started counting them" - } - }, - "clicks_7_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application over the last 7 days" - } - }, - "clicks_30_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application over the last 30 days" - } - }, - "clicks_90_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application over the last 90 days" - } - }, - "minutes_on_screen_total": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen since we started counting them." - } - }, - "minutes_on_screen_7_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen over the last 7 days" - } - }, - "minutes_on_screen_30_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen over the last 30 days" - } - }, - "minutes_on_screen_90_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen over the last 90 days" - } - }, - "views": { - "type": "array", - "items": { - "properties": { - "appId": { - "type": "keyword", - "_meta": { - "description": "The application being tracked" - } - }, - "viewId": { - "type": "keyword", - "_meta": { - "description": "The application view being tracked" - } - }, - "clicks_total": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application sub view since we started counting them" - } - }, - "clicks_7_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the active application sub view over the last 7 days" - } - }, - "clicks_30_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the active application sub view over the last 30 days" - } - }, - "clicks_90_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the active application sub view over the last 90 days" - } - }, - "minutes_on_screen_total": { - "type": "float", - "_meta": { - "description": "Minutes the application sub view is active and on-screen since we started counting them." - } - }, - "minutes_on_screen_7_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen active application sub view over the last 7 days" - } - }, - "minutes_on_screen_30_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen active application sub view over the last 30 days" - } - }, - "minutes_on_screen_90_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen active application sub view over the last 90 days" - } - } - } - } - } - } - }, "elasticsearchIndexManagement": { "properties": { "appId": { diff --git a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/services/extensions_service.ts b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/services/extensions_service.ts index 1e430e2b49189..b9bac9af10000 100644 --- a/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/services/extensions_service.ts +++ b/x-pack/platform/packages/shared/index-management/index_management_shared_types/src/services/extensions_service.ts @@ -30,9 +30,6 @@ export interface IndexBadge { filterExpression?: string; color: EuiBadgeProps['color']; } -export interface IndexDetailsPageRoute { - renderRoute: (indexName: string, detailsTabId?: string) => string; -} export interface EmptyListContent { renderContent: (args: { @@ -69,6 +66,4 @@ export interface ExtensionsSetup { addIndexDetailsTab(tab: IndexDetailsTab): void; // sets content to render instead of the code block on the overview tab of the index page setIndexOverviewContent(content: IndexContent): void; - // sets index details page route - setIndexDetailsPageRoute(route: IndexDetailsPageRoute, detailsTabId?: string): void; } 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 8224ad7985aed..cf0fd49c75665 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 @@ -83,6 +83,11 @@ export type IndexManagementLocatorParams = SerializableRecord & page: 'create_component_template'; componentTemplate: string; } + | { + page: 'index_details'; + indexName: string; + tab?: string; + } ); export type IndexManagementLocator = LocatorPublic; diff --git a/x-pack/platform/plugins/private/translations/translations/de-DE.json b/x-pack/platform/plugins/private/translations/translations/de-DE.json index 9594b27e7b45a..17a3566617dda 100644 --- a/x-pack/platform/plugins/private/translations/translations/de-DE.json +++ b/x-pack/platform/plugins/private/translations/translations/de-DE.json @@ -31293,8 +31293,6 @@ "xpack.searchIndices.connectionDetails.endpointTitle": "Elasticsearch-URL", "xpack.searchIndices.createIndex.docTitle": "Index erstellen", "xpack.searchIndices.documentsTabLabel": "Daten", - "xpack.searchIndices.elasticsearchIndices.createIndexTitle": "Index erstellen", - "xpack.searchIndices.elasticsearchIndices.startAppTitle": "Elasticsearch-Indizes", "xpack.searchIndices.guideSelectors.selectGuideTitle": "Wählen Sie einen Workflow-Leitfaden aus", "xpack.searchIndices.indexAction.ApiReferenceButtonLabel": "API-Referenz", "xpack.searchIndices.indexAction.useInPlaygroundButtonLabel": "Im Playground suchen", diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 01894f87e8750..5d61f143a5aed 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -31628,8 +31628,6 @@ "xpack.searchIndices.connectionDetails.endpointTitle": "URL Elasticsearch", "xpack.searchIndices.createIndex.docTitle": "Créer un index", "xpack.searchIndices.documentsTabLabel": "Données", - "xpack.searchIndices.elasticsearchIndices.createIndexTitle": "Créer un index", - "xpack.searchIndices.elasticsearchIndices.startAppTitle": "Index Elasticsearch", "xpack.searchIndices.guideSelectors.selectGuideTitle": "Sélectionner un guide de workflow", "xpack.searchIndices.indexAction.ApiReferenceButtonLabel": "Référence d'API", "xpack.searchIndices.indexAction.useInPlaygroundButtonLabel": "Rechercher dans Playground", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index 157b9918b72b2..651b39231f0a7 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -31686,8 +31686,6 @@ "xpack.searchIndices.connectionDetails.endpointTitle": "Elasticsearch URL", "xpack.searchIndices.createIndex.docTitle": "インデックスの作成", "xpack.searchIndices.documentsTabLabel": "データ", - "xpack.searchIndices.elasticsearchIndices.createIndexTitle": "インデックスの作成", - "xpack.searchIndices.elasticsearchIndices.startAppTitle": "Elasticsearchインデックス", "xpack.searchIndices.guideSelectors.selectGuideTitle": "ワークフローガイドを選択", "xpack.searchIndices.indexAction.ApiReferenceButtonLabel": "API参照", "xpack.searchIndices.indexAction.useInPlaygroundButtonLabel": "Playgroundで検索", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 1f5e573a8fa62..356952b0c41d2 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -31666,8 +31666,6 @@ "xpack.searchIndices.connectionDetails.endpointTitle": "Elasticsearch URL", "xpack.searchIndices.createIndex.docTitle": "创建索引", "xpack.searchIndices.documentsTabLabel": "数据", - "xpack.searchIndices.elasticsearchIndices.createIndexTitle": "创建索引", - "xpack.searchIndices.elasticsearchIndices.startAppTitle": "Elasticsearch 索引", "xpack.searchIndices.guideSelectors.selectGuideTitle": "选择工作流指南", "xpack.searchIndices.indexAction.ApiReferenceButtonLabel": "API 参考", "xpack.searchIndices.indexAction.useInPlaygroundButtonLabel": "在 Playground 中搜索", diff --git a/x-pack/platform/plugins/shared/index_management/README.md b/x-pack/platform/plugins/shared/index_management/README.md index 3649f75dec4b7..e6880d1952ec8 100644 --- a/x-pack/platform/plugins/shared/index_management/README.md +++ b/x-pack/platform/plugins/shared/index_management/README.md @@ -14,8 +14,6 @@ export interface EmptyListContent { }) => ReturnType; } ``` -- `setIndexDetailsPageRoute`: registers a new route for index details page in indices list table. For example, for serverless search users, navigating to an index on the indices list page will lead to the Search Indices detail page. - #### Extensions to the indices list and the index details page - `addAction(action: any)`: adds an option to the "manage index" menu, for example to add an ILM policy to the index 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 ecdbab7bb29bb..21e78d68d2ba1 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 @@ -10,7 +10,7 @@ */ import React from 'react'; import { screen, fireEvent, waitFor } from '@testing-library/react'; -import { applicationServiceMock, httpServiceMock } from '@kbn/core/public/mocks'; +import { httpServiceMock } from '@kbn/core/public/mocks'; import type { Index } from '../../../common'; import { API_BASE_PATH, INTERNAL_API_BASE_PATH } from '../../../common'; @@ -121,21 +121,15 @@ describe('', () => { createNonDataStreamIndex(indexName) ); - const application = applicationServiceMock.createStartContract(); - await renderHome(httpSetup, { - appServicesContext: { - core: { application }, - }, - }); + await renderHome(httpSetup); await screen.findByTestId('indexTable'); const tableActions = createIndexTableActions(); await tableActions.clickIndexNameAt(0); - expect(application.navigateToUrl).toHaveBeenCalledWith( - '/app/management/data/index_management/indices/index_details?indexName=testIndex&includeHiddenIndices=true' - ); + await screen.findByTestId('indexDetailsHeader'); + expect(screen.getByTestId('indexDetailsHeader')).toBeInTheDocument(); }); it('index page works with % character in index name', async () => { @@ -146,20 +140,18 @@ describe('', () => { createNonDataStreamIndex(indexName) ); - const application = applicationServiceMock.createStartContract(); - await renderHome(httpSetup, { - appServicesContext: { - core: { application }, - }, - }); + await renderHome(httpSetup); await screen.findByTestId('indexTable'); const tableActions = createIndexTableActions(); await tableActions.clickIndexNameAt(0); - expect(application.navigateToUrl).toHaveBeenCalledWith( - '/app/management/data/index_management/indices/index_details?indexName=test%25&includeHiddenIndices=true' + await screen.findByTestId('indexDetailsHeader'); + expect(screen.getByTestId('indexDetailsHeader')).toBeInTheDocument(); + expect(httpSetup.get).toHaveBeenCalledWith( + `${INTERNAL_API_BASE_PATH}/indices/${encodeURIComponent(indexName)}`, + expect.anything() ); }); @@ -588,42 +580,6 @@ describe('', () => { expect(screen.getByText('ILM managed')).toBeInTheDocument(); }); - it('renders to search_indices index details page', async () => { - const indexName = 'search-index'; - httpRequestsMockHelpers.setLoadIndicesResponse([createNonDataStreamIndex(indexName)]); - httpRequestsMockHelpers.setLoadIndexDetailsResponse( - indexName, - createNonDataStreamIndex(indexName) - ); - - const navigateToUrl = jest.fn(); - const url = `/app/elasticsearch/indices/index_details/${indexName}`; - await renderHome(httpSetup, { - appServicesContext: { - core: { - application: { navigateToUrl }, - }, - services: { - extensionsService: { - _indexDetailsPageRoute: { - renderRoute: () => { - return url; - }, - }, - }, - }, - }, - }); - - await screen.findByTestId('indexTable'); - - const tableActions = createIndexTableActions(); - await tableActions.clickIndexNameAt(0); - - 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'; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx index aa87a8ecb2a34..484997f8201a4 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx @@ -10,9 +10,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import { EuiButton } from '@elastic/eui'; -import useObservable from 'react-use/lib/useObservable'; import { CreateIndexModal } from './create_index_modal'; -import { useAppContext } from '../../../../app_context'; export interface CreateIndexButtonProps { loadIndices: () => void; @@ -21,18 +19,7 @@ export interface CreateIndexButtonProps { } export const CreateIndexButton = ({ loadIndices, share, dataTestSubj }: CreateIndexButtonProps) => { - const { - core: { chrome }, - } = useAppContext(); const [createIndexModalOpen, setCreateIndexModalOpen] = useState(false); - const createIndexUrl = share?.url.locators.get('SEARCH_CREATE_INDEX')?.useUrl({}); - - const activeSolutionId = useObservable(chrome.getActiveSolutionNavId$()); - - const actionProp = - createIndexUrl && activeSolutionId === 'es' - ? { href: createIndexUrl } - : { onClick: () => setCreateIndexModalOpen(true) }; return ( <> @@ -42,7 +29,7 @@ export const CreateIndexButton = ({ loadIndices, share, dataTestSubj }: CreateIn key="createIndexButton" data-test-subj={dataTestSubj || 'createIndexButton'} data-telemetry-id="idxMgmt-indexList-createIndexButton" - {...actionProp} + onClick={() => setCreateIndexModalOpen(true)} > {showApi && ( - + {apiCode} )} diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/quick_stats/status_details.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/quick_stats/status_details.tsx index da33d30a709bd..8df3b2fcb0269 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/quick_stats/status_details.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/details_page/quick_stats/status_details.tsx @@ -67,7 +67,11 @@ export const StatusDetails: FunctionComponent<{ } const badgeConfig = healthToBadgeMapping[health.toLowerCase() as NormalizedHealth]; - const healthBadge = {badgeConfig.label}; + const healthBadge = ( + + {badgeConfig.label} + + ); const renderDocCountFooter = () => { if (docCount.isLoading) { @@ -95,7 +99,7 @@ export const StatusDetails: FunctionComponent<{ } return ( - + diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.test.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.test.tsx index ce19e20e607db..0995d1b7ae9d1 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.test.tsx @@ -16,7 +16,7 @@ import type { AppDependencies } from '../../../../app_context'; import { IndexActionsContextMenu } from './index_actions_context_menu'; import type { Index } from '@kbn/index-management-shared-types'; import { notificationService } from '../../../../services/notification'; -import { navigateToIndexDetailsPage, getIndexDetailsLink } from '../../../../services/routing'; +import { getIndexDetailsLink } from '../../../../services/routing'; import { type DocCountResult, RequestResultType } from '../index_table/get_doc_count'; // EUI context menus keep inactive panels mounted with `pointer-events: none`, @@ -26,7 +26,6 @@ const user = userEvent.setup({ pointerEventsCheck: 0, delay: null }); jest.mock('../../../../services/routing', () => ({ ...jest.requireActual('../../../../services/routing'), getIndexDetailsLink: jest.fn(() => '/indices/some/stats'), - navigateToIndexDetailsPage: jest.fn(), })); jest.mock('../../../../services/notification', () => ({ @@ -395,8 +394,8 @@ describe('IndexActionsContextMenu', () => { }); describe('WHEN navigating to index detail pages', () => { - describe('AND WHEN clicking Overview/Settings/Mapping', () => { - it('SHOULD call navigateToIndexDetailsPage for each navigation', async () => { + describe('AND WHEN clicking Overview/Settings/Mapping/Stats', () => { + it('SHOULD use history.push with getIndexDetailsLink for each navigation', async () => { const props = getBaseProps(); const historyPush = jest.fn(); const ctx = getIndexManagementCtx({ @@ -425,32 +424,13 @@ describe('IndexActionsContextMenu', () => { const mappingBtn = await within(menu3).findByText(/show index mapping/i); await user.click(mappingBtn); - expect(navigateToIndexDetailsPage).toHaveBeenCalledTimes(3); - }); - }); - - describe('AND WHEN clicking Stats', () => { - it('SHOULD use history.push with getIndexDetailsLink', async () => { - const props = getBaseProps(); - const historyPush = jest.fn(); - const ctx = getIndexManagementCtx({ - history: { push: historyPush } as unknown as AppDependencies['history'], - }); - render( - - - - - - ); - await openContextMenu(); - const menu = await screen.findByTestId('indexContextMenu'); - const statsBtn = await within(menu).findByText(/show index stats/i); - + const menu4 = await screen.findByTestId('indexContextMenu'); + const statsBtn = await within(menu4).findByText(/show index stats/i); await user.click(statsBtn); - expect(getIndexDetailsLink).toHaveBeenCalled(); + expect(getIndexDetailsLink).toHaveBeenCalledTimes(4); + expect(historyPush).toHaveBeenCalledTimes(4); expect(historyPush).toHaveBeenCalledWith('/indices/some/stats'); }); }); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.tsx index 786183b164ea1..fb3b2a22db3e0 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.tsx @@ -31,7 +31,7 @@ import { MAX_DOCUMENTS_FOR_CONVERT_TO_LOOKUP_INDEX, MAX_SHARDS_FOR_CONVERT_TO_LOOKUP_INDEX, } from '../../../../../../common/constants'; -import { getIndexDetailsLink, navigateToIndexDetailsPage } from '../../../../services/routing'; +import { getIndexDetailsLink } from '../../../../services/routing'; import { useAppContext } from '../../../../app_context'; import type { Index } from '../../../../../../common'; import { type DocCountApi, RequestResultType } from '../index_table/get_doc_count'; @@ -116,7 +116,7 @@ export const IndexActionsContextMenu = ({ const { services: { extensionsService }, plugins: { reindexService }, - core: { getUrlForApp, application, http }, + core: { getUrlForApp }, history, config: { enableIndexActions, isServerless }, } = useAppContext(); @@ -218,16 +218,9 @@ export const IndexActionsContextMenu = ({ defaultMessage: 'Show index overview', }), onClick: () => { - closePopoverAndExecute(() => { - navigateToIndexDetailsPage( - indexNames[0], - indicesListURLParams, - extensionsService, - application, - http, - IndexDetailsSection.Overview - ); - }); + history.push( + getIndexDetailsLink(indexNames[0], indicesListURLParams, IndexDetailsSection.Overview) + ); }, }); items.push({ @@ -236,16 +229,9 @@ export const IndexActionsContextMenu = ({ defaultMessage: 'Show index settings', }), onClick: () => { - closePopoverAndExecute(() => { - navigateToIndexDetailsPage( - indexNames[0], - indicesListURLParams, - extensionsService, - application, - http, - IndexDetailsSection.Settings - ); - }); + history.push( + getIndexDetailsLink(indexNames[0], indicesListURLParams, IndexDetailsSection.Settings) + ); }, }); items.push({ @@ -254,16 +240,9 @@ export const IndexActionsContextMenu = ({ defaultMessage: 'Show index mapping', }), onClick: () => { - closePopoverAndExecute(() => { - navigateToIndexDetailsPage( - indexNames[0], - indicesListURLParams, - extensionsService, - application, - http, - IndexDetailsSection.Mappings - ); - }); + history.push( + getIndexDetailsLink(indexNames[0], indicesListURLParams, IndexDetailsSection.Mappings) + ); }, }); if (allOpen && enableIndexActions) { @@ -485,8 +464,7 @@ export const IndexActionsContextMenu = ({ reloadIndices={reloadIndices} extensionsService={extensionsService} getUrlForApp={getUrlForApp} - application={application} - http={http} + history={history} /> ({ ...jest.requireActual('../../../../../services/routing'), - navigateToIndexDetailsPage: jest.fn(), + getIndexDetailsLink: jest.fn().mockReturnValue('/mocked-link'), })); jest.mock('../../../../../services/notification', () => ({ @@ -59,8 +59,7 @@ const baseProps: React.ComponentProps = { toggles: [], } as unknown as React.ComponentProps['extensionsService'], getUrlForApp: jest.fn() as React.ComponentProps['getUrlForApp'], - application: {} as React.ComponentProps['application'], - http: {} as React.ComponentProps['http'], + history: { push: jest.fn() } as unknown as React.ComponentProps['history'], }; const renderWithI18n = (ui: React.ReactElement) => { @@ -276,7 +275,8 @@ describe('ModalHost', () => { const success = await screen.findByTestId('convert-success'); await userEvent.click(success); - expect(navigateToIndexDetailsPage).toHaveBeenCalled(); + expect(getIndexDetailsLink).toHaveBeenCalled(); + expect(baseProps.history.push).toHaveBeenCalledWith('/mocked-link'); expect(notificationService.showSuccessToast).toHaveBeenCalled(); }); }); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/modal_host/modal_host.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/modal_host/modal_host.tsx index d701ca20b008b..46e0fc34a86c8 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/modal_host/modal_host.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/index_actions_context_menu/modal_host/modal_host.tsx @@ -17,13 +17,12 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import type { HttpSetup } from '@kbn/core-http-browser'; -import type { ApplicationStart } from '@kbn/core/public'; +import type { ApplicationStart, ScopedHistory } from '@kbn/core/public'; import type { ExtensionsService } from '../../../../../../services/extensions_service'; import { ConvertToLookupIndexModalContainer } from '../../details_page/convert_to_lookup_index_modal/convert_to_lookup_index_modal_container'; -import { navigateToIndexDetailsPage } from '../../../../../services/routing'; import { notificationService } from '../../../../../services/notification'; +import { getIndexDetailsLink } from '../../../../../services/routing'; import { IndexDetailsSection } from '../../../../../../../common/constants'; import type { Index } from '../../../../../../../common'; @@ -88,8 +87,7 @@ export interface ModalHostProps { reloadIndices: () => void; extensionsService: ExtensionsService; // concrete type from services getUrlForApp: ApplicationStart['getUrlForApp']; - application: ApplicationStart; - http: HttpSetup; + history: ScopedHistory; } export const ModalHost = memo( @@ -104,8 +102,7 @@ export const ModalHost = memo( reloadIndices, extensionsService, getUrlForApp, - application, - http, + history, }, ref ) { @@ -316,13 +313,12 @@ export const ModalHost = memo( closeConfirmModal()} onSuccess={(lookupIndexName) => { - navigateToIndexDetailsPage( - lookupIndexName, - indicesListURLParams, - extensionsService, - application, - http, - IndexDetailsSection.Overview + history.push( + getIndexDetailsLink( + lookupIndexName, + indicesListURLParams, + IndexDetailsSection.Overview + ) ); notificationService.showSuccessToast( 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 cc84b8b6bcb24..7b650693d85ac 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 @@ -44,7 +44,7 @@ import { attemptToURIDecode, } from '../../../../../shared_imports'; import { formatBytes } from '../../../../..'; -import { getDataStreamDetailsLink, navigateToIndexDetailsPage } from '../../../../services/routing'; +import { getDataStreamDetailsLink, getIndexDetailsLink } from '../../../../services/routing'; import { documentationService } from '../../../../services/documentation'; import { AppContextConsumer } from '../../../../app_context'; import { renderBadges } from '../../../../lib/render_badges'; @@ -62,8 +62,6 @@ const getColumnConfigs = ({ filterChanged, extensionsService, location, - application, - http, docCountApi, }) => { const columns = [ @@ -80,15 +78,7 @@ const getColumnConfigs = ({ <> { - navigateToIndexDetailsPage( - index.name, - location.search || '', - extensionsService, - application, - http - ); - }} + onClick={() => history.push(getIndexDetailsLink(index.name, location.search || ''))} > {index.name} diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/routing.test.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/routing.test.ts index 86a6875934720..24500cb6059bf 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/routing.test.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/routing.test.ts @@ -5,16 +5,10 @@ * 2.0. */ -import { getIndexDetailsLink, getIndexListUri, navigateToIndexDetailsPage } from './routing'; -import { applicationServiceMock, httpServiceMock } from '@kbn/core/public/mocks'; -import type { ExtensionsService } from '../../services/extensions_service'; -import { IndexDetailsSection } from '../../../common/constants'; +import { getIndexDetailsLink, getIndexListUri } from './routing'; describe('routing', () => { describe('index details link', () => { - const application = applicationServiceMock.createStartContract(); - const http = httpServiceMock.createSetupContract(); - it('adds the index name to the url', () => { const indexName = 'testIndex'; const url = getIndexDetailsLink(indexName, ''); @@ -32,33 +26,6 @@ describe('routing', () => { const url = getIndexDetailsLink('testIndex', '', tab); expect(url).toContain(`tab=${tab}`); }); - it('renders default index details route without extensionService indexDetailsPageRoute ', () => { - const extensionService = { - indexDetailsPageRoute: null, - } as ExtensionsService; - navigateToIndexDetailsPage('testIndex', '', extensionService, application, http); - expect(application.navigateToUrl).toHaveBeenCalled(); - }); - - it('renders route from extensionService indexDetailsPageRoute with tab id', () => { - const extensionService = { - indexDetailsPageRoute: { - renderRoute: (indexName: string, detailsTabId?: string) => { - return `test_url/${detailsTabId}`; - }, - }, - } as ExtensionsService; - navigateToIndexDetailsPage( - 'testIndex', - '', - extensionService, - application, - http, - IndexDetailsSection.Settings - ); - expect(application.navigateToUrl).toHaveBeenCalled(); - expect(application.navigateToUrl).toHaveBeenCalledWith('test_url/settings'); - }); }); describe('indices list link', () => { diff --git a/x-pack/platform/plugins/shared/index_management/public/application/services/routing.ts b/x-pack/platform/plugins/shared/index_management/public/application/services/routing.ts index 93458ccdc56e7..1df70f2144c3e 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/services/routing.ts +++ b/x-pack/platform/plugins/shared/index_management/public/application/services/routing.ts @@ -5,12 +5,8 @@ * 2.0. */ -import type { ApplicationStart } from '@kbn/core/public'; -import type { HttpSetup } from '@kbn/core/public'; import { Section } from '../../../common/constants'; import type { IndexDetailsTabId } from '../../../common/constants'; -import type { ExtensionsService } from '../../services/extensions_service'; -import type { IndexDetailsSection } from '../../../common/constants'; export const getTemplateListLink = () => `/templates`; @@ -108,27 +104,3 @@ export const getComponentTemplateCloneLink = (name: string) => { export const getComponentTemplateCreateLink = (name: string) => { return `/create_component_template?name=${encodeURIComponent(name)}`; }; - -export const navigateToIndexDetailsPage = ( - indexName: string, - indicesListURLParams: string, - extensionsService: ExtensionsService, - application: ApplicationStart, - http: HttpSetup, - tabId?: IndexDetailsSection -) => { - if (!extensionsService.indexDetailsPageRoute) { - application.navigateToUrl( - http.basePath.prepend( - `/app/management/data/index_management${getIndexDetailsLink( - indexName, - indicesListURLParams, - tabId - )}` - ) - ); - } else { - const route = extensionsService.indexDetailsPageRoute.renderRoute(indexName, tabId); - application.navigateToUrl(http.basePath.prepend(route)); - } -}; diff --git a/x-pack/platform/plugins/shared/index_management/public/locator.ts b/x-pack/platform/plugins/shared/index_management/public/locator.ts index e6b10da490ba6..8909426d393ba 100644 --- a/x-pack/platform/plugins/shared/index_management/public/locator.ts +++ b/x-pack/platform/plugins/shared/index_management/public/locator.ts @@ -18,6 +18,7 @@ import { getComponentTemplateEditLink, getComponentTemplateListLink, getDataStreamDetailsLink, + getIndexDetailsLink, getIndexListUri, getTemplateCreateLink, getTemplateCloneLink, @@ -117,6 +118,12 @@ export class IndexManagementLocatorDefinition path: location.path + getComponentTemplateCreateLink(params.componentTemplate), }; } + case 'index_details': { + return { + ...location, + path: location.path + getIndexDetailsLink(params.indexName, '', params.tab), + }; + } } }; } diff --git a/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.mock.ts b/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.mock.ts index 2aafe6673ce07..d12375642f7f9 100644 --- a/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.mock.ts +++ b/x-pack/platform/plugins/shared/index_management/public/services/extensions_service.mock.ts @@ -21,7 +21,6 @@ const createServiceMock = (): ExtensionsSetupMock => ({ setEmptyListContent: jest.fn(), addIndexDetailsTab: jest.fn(), setIndexOverviewContent: jest.fn(), - setIndexDetailsPageRoute: jest.fn(), }); const createMock = () => { 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 7dfa7e63287ca..1e23d95e8eea0 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 @@ -13,7 +13,6 @@ import type { EmptyListContent, IndexContent, ExtensionsSetup, - IndexDetailsPageRoute, } from '@kbn/index-management-shared-types'; import type { IndexDetailsTab } from '../../common/constants'; @@ -48,7 +47,6 @@ export class ExtensionsService { private _emptyListContent: EmptyListContent | null = null; private _indexDetailsTabs: IndexDetailsTab[] = []; private _indexOverviewContent: IndexContent | null = null; - private _indexDetailsPageRoute: IndexDetailsPageRoute | null = null; private service?: ExtensionsSetup; public setup(): ExtensionsSetup { @@ -62,7 +60,6 @@ export class ExtensionsService { setEmptyListContent: this.setEmptyListContent.bind(this), addIndexDetailsTab: this.addIndexDetailsTab.bind(this), setIndexOverviewContent: this.setIndexOverviewContent.bind(this), - setIndexDetailsPageRoute: this.setIndexDetailsPageRoute.bind(this), }; return this.service; @@ -112,14 +109,6 @@ export class ExtensionsService { } } - private setIndexDetailsPageRoute(route: IndexDetailsPageRoute) { - if (this._indexDetailsPageRoute) { - throw new Error(`The route for index details has already been set.`); - } else { - this._indexDetailsPageRoute = route; - } - } - public get actions() { return this._actions; } @@ -155,8 +144,4 @@ export class ExtensionsService { public get indexOverviewContent() { return this._indexOverviewContent; } - - public get indexDetailsPageRoute() { - return this._indexDetailsPageRoute; - } } diff --git a/x-pack/platform/plugins/shared/search_inference_endpoints/moon.yml b/x-pack/platform/plugins/shared/search_inference_endpoints/moon.yml index d48db61f45bfb..b3f5d262cb464 100644 --- a/x-pack/platform/plugins/shared/search_inference_endpoints/moon.yml +++ b/x-pack/platform/plugins/shared/search_inference_endpoints/moon.yml @@ -34,7 +34,6 @@ dependsOn: - '@kbn/kibana-utils-plugin' - '@kbn/features-plugin' - '@kbn/ui-theme' - - '@kbn/deeplinks-search' - '@kbn/deeplinks-management' - '@kbn/management-plugin' - '@kbn/serverless' diff --git a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.test.tsx b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.test.tsx index bd3013a9c4e58..f96837f21ea0e 100644 --- a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.test.tsx +++ b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.test.tsx @@ -5,16 +5,19 @@ * 2.0. */ -import { render, fireEvent, screen } from '@testing-library/react'; +import { render, fireEvent, screen, waitFor } from '@testing-library/react'; import React from 'react'; +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; + import { IndexItem } from './index_item'; import type { InferenceUsageInfo } from '../../../../types'; import { useKibana } from '../../../../../../hooks/use_kibana'; jest.mock('../../../../../../hooks/use_kibana'); const mockUseKibana = useKibana as jest.Mock; -const mockNavigateToApp = jest.fn(); +const locatorMock = sharePluginMock.createLocator(); +let mockLocatorGet: jest.Mock; describe('Index Item', () => { const item: InferenceUsageInfo = { @@ -22,13 +25,21 @@ describe('Index Item', () => { type: 'Index', }; beforeEach(() => { + jest.resetAllMocks(); + locatorMock.getUrl.mockResolvedValue('https://locator.url'); + mockLocatorGet = jest.fn().mockReturnValue(locatorMock); mockUseKibana.mockReturnValue({ services: { - application: { - navigateToApp: mockNavigateToApp, + share: { + url: { + locators: { + get: mockLocatorGet, + }, + }, }, }, }); + jest.spyOn(window, 'open').mockImplementation(() => null); render(); }); @@ -38,11 +49,16 @@ describe('Index Item', () => { expect(screen.getByText('Index')).toBeInTheDocument(); }); - it('opens index in a new tab', () => { + it('opens index in a new tab', async () => { fireEvent.click(screen.getByRole('button')); - expect(mockNavigateToApp).toHaveBeenCalledWith('enterpriseSearchContent', { - openInNewTab: true, - path: 'search_indices/index-1', + + expect(mockLocatorGet).toHaveBeenCalledWith('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); + await waitFor(() => { + expect(locatorMock.getUrl).toHaveBeenCalledWith({ + page: 'index_details', + indexName: 'index-1', + }); }); + expect(window.open).toHaveBeenCalledWith('https://locator.url', '_blank'); }); }); diff --git a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.tsx b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.tsx index 43911dd611b0e..04f2ee4901388 100644 --- a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.tsx +++ b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/index_item.tsx @@ -17,34 +17,29 @@ import { EuiSpacer, } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { ENTERPRISE_SEARCH_CONTENT_APP_ID } from '@kbn/deeplinks-search'; - -import { SEARCH_INDICES } from '@kbn/deeplinks-search/constants'; import { useKibana } from '../../../../../../hooks/use_kibana'; import type { InferenceUsageInfo } from '../../../../types'; -import { SERVERLESS_INDEX_MANAGEMENT_URL } from '../../../../constants'; interface UsageProps { usageItem: InferenceUsageInfo; } export const IndexItem: React.FC = ({ usageItem }) => { const { - services: { application, serverless }, + services: { share }, } = useKibana(); - const navigateToIndex = useCallback(() => { - if (serverless) { - application?.navigateToApp(SEARCH_INDICES, { - path: `${SERVERLESS_INDEX_MANAGEMENT_URL}/${usageItem.id}/data`, - openInNewTab: true, - }); - } else { - application?.navigateToApp(ENTERPRISE_SEARCH_CONTENT_APP_ID, { - path: `search_indices/${usageItem.id}`, - openInNewTab: true, + const navigateToIndex = useCallback(async () => { + if (!usageItem?.id) return; + + const indexManagementLocator = share?.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); + if (indexManagementLocator) { + const url = await indexManagementLocator.getUrl({ + page: 'index_details', + indexName: usageItem.id, }); + if (url) window.open(url, '_blank'); } - }, [application, serverless, usageItem.id]); + }, [share, usageItem.id]); return ( @@ -64,7 +59,7 @@ export const IndexItem: React.FC = ({ usageItem }) => { - + diff --git a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx index a9cb3f1f8d389..9cdd3b090e50b 100644 --- a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx +++ b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { render, fireEvent, screen } from '@testing-library/react'; +import { render, fireEvent, screen, waitFor } from '@testing-library/react'; import React from 'react'; import { ScanUsageResults } from './scan_usage_results'; @@ -13,7 +13,8 @@ import { useKibana } from '../../../../../../hooks/use_kibana'; jest.mock('../../../../../../hooks/use_kibana'); const mockUseKibana = useKibana as jest.Mock; -const mockNavigateToApp = jest.fn(); +const mockGetUrl = jest.fn(); +const mockLocatorGet = jest.fn(); const mockOnCheckboxChange = jest.fn(); describe('ScanUsageResults', () => { @@ -28,14 +29,24 @@ describe('ScanUsageResults', () => { }, ]; beforeEach(() => { + jest.clearAllMocks(); + mockGetUrl.mockResolvedValue('/index_management/indices'); + mockLocatorGet.mockReturnValue({ getUrl: mockGetUrl }); + mockUseKibana.mockReturnValue({ services: { - application: { - navigateToApp: mockNavigateToApp, + share: { + url: { + locators: { + get: mockLocatorGet, + }, + }, }, }, }); + jest.spyOn(window, 'open').mockImplementation(() => null); + render( { ); }); + afterEach(() => { + jest.restoreAllMocks(); + }); + it('renders', () => { expect(screen.getByText('Potential Failures')).toBeInTheDocument(); expect(screen.getByText('Found 2 usages')).toBeInTheDocument(); @@ -56,11 +71,14 @@ describe('ScanUsageResults', () => { expect(checkbox).toHaveProperty('checked', false); }); - it('opens index management in a new tab', () => { + it('opens index management in a new tab', async () => { fireEvent.click(screen.getByTestId('inferenceManagementOpenIndexManagement')); - expect(mockNavigateToApp).toHaveBeenCalledWith('enterpriseSearchContent', { - openInNewTab: true, - path: 'search_indices', + + expect(mockLocatorGet).toHaveBeenCalledWith('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); + expect(mockGetUrl).toHaveBeenCalledWith({ page: 'index_list' }); + + await waitFor(() => { + expect(window.open).toHaveBeenCalledWith('/index_management/indices', '_blank'); }); }); diff --git a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx index a6b10719e833a..9cc9da74f427f 100644 --- a/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx +++ b/x-pack/platform/plugins/shared/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx @@ -13,13 +13,10 @@ import { EuiPanel, EuiText, EuiButtonEmpty, + useEuiTheme, } from '@elastic/eui'; import React from 'react'; -import { euiThemeVars } from '@kbn/ui-theme'; import { css } from '@emotion/react'; -import { ENTERPRISE_SEARCH_CONTENT_APP_ID } from '@kbn/deeplinks-search'; - -import { SEARCH_INDICES } from '@kbn/deeplinks-search/constants'; import type { InferenceUsageInfo } from '../../../../types'; import { useKibana } from '../../../../../../hooks/use_kibana'; import { RenderMessageWithIcon } from './render_message_with_icon'; @@ -38,18 +35,14 @@ export const ScanUsageResults: React.FC = ({ onIgnoreWarningCheckboxChange, }) => { const { - services: { application, serverless }, + services: { share }, } = useKibana(); - const handleNavigateToIndexManagement = () => { - if (serverless) { - application?.navigateToApp(SEARCH_INDICES, { - openInNewTab: true, - }); - } else { - application?.navigateToApp(ENTERPRISE_SEARCH_CONTENT_APP_ID, { - path: `search_indices`, - openInNewTab: true, - }); + const { euiTheme } = useEuiTheme(); + const handleNavigateToIndexManagement = async () => { + const indexManagementLocator = share?.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); + if (indexManagementLocator) { + const url = await indexManagementLocator.getUrl({ page: 'index_list' }); + if (url) window.open(url, '_blank'); } }; @@ -72,7 +65,7 @@ export const ScanUsageResults: React.FC = ({

{i18n.COUNT_USAGE_LABEL(list.length)}

diff --git a/x-pack/platform/plugins/shared/search_inference_endpoints/tsconfig.json b/x-pack/platform/plugins/shared/search_inference_endpoints/tsconfig.json index c761a4adc699d..7954a92cddfb5 100644 --- a/x-pack/platform/plugins/shared/search_inference_endpoints/tsconfig.json +++ b/x-pack/platform/plugins/shared/search_inference_endpoints/tsconfig.json @@ -27,7 +27,6 @@ "@kbn/kibana-utils-plugin", "@kbn/features-plugin", "@kbn/ui-theme", - "@kbn/deeplinks-search", "@kbn/deeplinks-management", "@kbn/management-plugin", "@kbn/serverless", diff --git a/x-pack/platform/test/functional/config.base.ts b/x-pack/platform/test/functional/config.base.ts index c2d17091d169b..73ceafa077ab5 100644 --- a/x-pack/platform/test/functional/config.base.ts +++ b/x-pack/platform/test/functional/config.base.ts @@ -197,12 +197,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { obsAIAssistantManagement: { pathname: '/app/management/ai/observabilityAiAssistantManagement', }, - enterpriseSearch: { - pathname: '/app/elasticsearch/overview', - }, - elasticsearchIndices: { - pathname: '/app/elasticsearch/indices', - }, searchPlayground: { pathname: '/app/search_playground', }, diff --git a/x-pack/platform/test/functional/page_objects/index_management_page.ts b/x-pack/platform/test/functional/page_objects/index_management_page.ts index 07a95ada4845c..1732bc029aebc 100644 --- a/x-pack/platform/test/functional/page_objects/index_management_page.ts +++ b/x-pack/platform/test/functional/page_objects/index_management_page.ts @@ -19,10 +19,6 @@ export function IndexManagementPageProvider({ getService, getPageObjects }: FtrP return await testSubjects.getVisibleText('appTitle'); }, - async expectToBeOnSearchIndexManagement() { - await testSubjects.existOrFail('elasticsearchIndexManagement'); - }, - async expectToBeOnIndexManagement() { const headingText = await testSubjects.getVisibleText('appTitle'); expect(headingText).to.be('Index Management'); @@ -251,6 +247,17 @@ export function IndexManagementPageProvider({ getService, getPageObjects }: FtrP expect(indexNames.some((i) => i === indexName)).to.be(true); }, + async clickCreateIndexShowApiButton() { + await testSubjects.existOrFail('createIndexShowApiButton'); + await testSubjects.click('createIndexShowApiButton'); + }, + async getCreateIndexShowApiButtonText() { + return await testSubjects.getVisibleText('createIndexShowApiButton'); + }, + async getCreateIndexApiCodeBlockContent() { + await testSubjects.existOrFail('createIndexApiCodeBlock'); + return await testSubjects.getVisibleText('createIndexApiCodeBlock'); + }, async confirmDeleteModalIsVisible() { await testSubjects.existOrFail('deleteIndexMenuButton'); await testSubjects.click('deleteIndexMenuButton'); diff --git a/x-pack/platform/test/serverless/functional/config.search.base.ts b/x-pack/platform/test/serverless/functional/config.search.base.ts index b694aa46045b5..8698d3d58efed 100644 --- a/x-pack/platform/test/serverless/functional/config.search.base.ts +++ b/x-pack/platform/test/serverless/functional/config.search.base.ts @@ -38,9 +38,6 @@ export default createTestConfig({ searchPlayground: { pathname: '/app/search_playground', }, - elasticsearchIndices: { - pathname: '/app/elasticsearch/indices', - }, searchInferenceEndpoints: { pathname: '/app/management/ml/inference_endpoints', }, diff --git a/x-pack/solutions/search/plugins/enterprise_search/common/constants.ts b/x-pack/solutions/search/plugins/enterprise_search/common/constants.ts index 7129ab971c6c8..73146bb6ccfad 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/common/constants.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/common/constants.ts @@ -13,9 +13,9 @@ import { ENTERPRISE_SEARCH_APPLICATIONS_APP_ID, ENTERPRISE_SEARCH_ANALYTICS_APP_ID, SEARCH_HOMEPAGE, - SEARCH_INDICES, SEARCH_INDEX_MANAGEMENT, } from '@kbn/deeplinks-search'; +import type { SearchIndexManagement } from '@kbn/deeplinks-search/deep_links'; import { i18n } from '@kbn/i18n'; import type { IngestPipelineParams } from '@kbn/search-connectors'; @@ -29,7 +29,7 @@ export const ENTERPRISE_SEARCH_PRODUCT_NAME = i18n.translate('xpack.enterpriseSe defaultMessage: 'Enterprise Search', }); -export { SEARCH_INDICES, SEARCH_INDEX_MANAGEMENT, SEARCH_HOMEPAGE }; +export { SEARCH_HOMEPAGE }; export const ENTERPRISE_SEARCH_HOME_PLUGIN = { ID: ENTERPRISE_SEARCH_APP_ID, @@ -179,3 +179,6 @@ export const SEARCH_APPS_TITLE = i18n.translate( defaultMessage: 'Search applications', } ); + +export const SEARCH_INDEX_MANAGEMENT_APP_ID: SearchIndexManagement = SEARCH_INDEX_MANAGEMENT; +export const SEARCH_INDEX_MANAGEMENT_APP_BASE = '/app/elasticsearch/index_management'; diff --git a/x-pack/solutions/search/plugins/enterprise_search/kibana.jsonc b/x-pack/solutions/search/plugins/enterprise_search/kibana.jsonc index 073ba29967cc6..8b50f34f76e57 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/kibana.jsonc +++ b/x-pack/solutions/search/plugins/enterprise_search/kibana.jsonc @@ -16,6 +16,7 @@ "requiredPlugins": [ "data", "features", + "indexManagement", "licensing", "logsShared", "logsDataAccess", @@ -29,7 +30,6 @@ "customIntegrations", "globalSearch", "home", - "indexManagement", "ml", "security", "spaces", diff --git a/x-pack/solutions/search/plugins/enterprise_search/moon.yml b/x-pack/solutions/search/plugins/enterprise_search/moon.yml index 43f3ca16cbb06..0716120f68e13 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/moon.yml +++ b/x-pack/solutions/search/plugins/enterprise_search/moon.yml @@ -87,6 +87,7 @@ dependsOn: - '@kbn/charts-theme' - '@kbn/licensing-types' - '@kbn/deeplinks-management' + - '@kbn/react-kibana-context-render' tags: - plugin - prod diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_indices.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_indices.tsx index 112487620ae55..65257feb8e9fa 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_indices.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_indices.tsx @@ -39,8 +39,8 @@ export const SearchApplicationIndices: React.FC = () => { const { removeIndexFromSearchApplication } = useActions(SearchApplicationIndicesLogic); const { navigateToUrl, share } = useValues(KibanaLogic); const [removeIndexConfirm, setConfirmRemoveIndex] = useState(null); - const searchIndicesLocator = useMemo( - () => share?.url.locators.get('SEARCH_INDEX_DETAILS_LOCATOR_ID'), + const indexManagementLocator = useMemo( + () => share?.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'), [share] ); @@ -116,7 +116,7 @@ export const SearchApplicationIndices: React.FC = () => { ), render: (health: 'red' | 'green' | 'yellow' | 'unavailable') => ( - +  {health ?? '-'} ), @@ -146,7 +146,7 @@ export const SearchApplicationIndices: React.FC = () => { { actions: [ { - enabled: () => searchIndicesLocator !== undefined, + enabled: () => indexManagementLocator !== undefined, available: (index) => index.health !== 'unknown', 'data-test-subj': 'search-application-view-index-btn', description: i18n.translate( @@ -169,8 +169,11 @@ export const SearchApplicationIndices: React.FC = () => { ), onClick: async (index) => { - if (searchIndicesLocator) { - const url = await searchIndicesLocator.getUrl({ indexName: index.name }); + if (indexManagementLocator) { + const url = await indexManagementLocator.getUrl({ + indexName: index.name, + page: 'index_details', + }); navigateToUrl(url, { shouldNotCreateHref: true, shouldNotPrepend: true, diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_schema.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_schema.tsx index 25cbab7fc8c8b..e723a0ff0b048 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_schema.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_schema.tsx @@ -36,21 +36,32 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { FieldIcon } from '@kbn/react-field'; -import { ENTERPRISE_SEARCH_DATA_PLUGIN } from '../../../../../common/constants'; import type { SchemaField } from '../../../../../common/types/search_applications'; -import { SEARCH_INDEX_TAB_PATH } from '../../../enterprise_search_content/routes'; import { docLinks } from '../../../shared/doc_links'; -import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { SearchApplicationViewLogic } from './search_application_view_logic'; const SchemaFieldDetails: React.FC<{ schemaField: SchemaField }> = ({ schemaField }) => { - const { navigateToUrl } = useValues(KibanaLogic); + const { share } = useValues(KibanaLogic); + const indexManagementLocator = useMemo( + () => share?.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'), + [share] + ); const notInAllIndices = schemaField.indices.some((i) => i.type === 'unmapped'); + const navigateToIndexMappings = useCallback( + (indexName: string) => { + indexManagementLocator?.navigate({ + indexName, + page: 'index_details', + tab: 'mappings', + }); + }, + [indexManagementLocator] + ); + const columns: Array> = [ { field: 'name', @@ -60,16 +71,17 @@ const SchemaFieldDetails: React.FC<{ schemaField: SchemaField }> = ({ schemaFiel defaultMessage: 'Parent index', } ), - render: (name: string) => ( - - {name} - - ), + render: (name: string) => + indexManagementLocator ? ( + navigateToIndexMappings(name)} + > + {name} + + ) : ( + name + ), }, { field: 'type', @@ -92,25 +104,21 @@ const SchemaFieldDetails: React.FC<{ schemaField: SchemaField }> = ({ schemaFiel return name; }, }, - { + ]; + + if (indexManagementLocator) { + columns.push({ actions: [ { description: 'View index mappings', icon: 'eye', name: 'View index', - onClick: (item: SchemaField['indices'][0]) => { - navigateToUrl( - generateEncodedPath(SEARCH_INDEX_TAB_PATH, { - indexName: item.name, - tabId: 'index_mappings', - }) - ); - }, + onClick: (item: SchemaField['indices'][0]) => navigateToIndexMappings(item.name), type: 'icon', }, ], - }, - ]; + }); + } return ( diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view_index_link.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view_index_link.tsx index 47b454c43cc24..24af3c8d5a11c 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view_index_link.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view_index_link.tsx @@ -13,14 +13,16 @@ import { EuiLink } from '@elastic/eui'; import { KibanaLogic } from '../../../shared/kibana'; export const SearchApplicationViewIndexLink: React.FC<{ - indexName: string; - dataTestSubj?: string; dataTelemetryId?: string; + dataTestSubj?: string; + indexName: string; }> = ({ indexName, dataTestSubj, dataTelemetryId }) => { const { share } = useValues(KibanaLogic); const searchIndexDetailsUrl: string = - share?.url.locators.get('SEARCH_INDEX_DETAILS_LOCATOR_ID')?.useUrl({ indexName }) ?? ''; + share?.url.locators + .get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID') + ?.useUrl({ indexName, page: 'index_details' }) ?? ''; return searchIndexDetailsUrl ? ( = ({ onDelete, }) => { const { navigateToUrl, share } = useValues(KibanaLogic); - const searchIndicesLocator = useMemo( - () => share?.url.locators.get('SEARCH_INDEX_DETAILS_LOCATOR_ID'), + const indexManagementLocator = useMemo( + () => share?.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'), [share] ); @@ -88,7 +88,7 @@ export const ConnectorsTable: React.FC = ({ ), render: (connector: ConnectorViewItem) => connector.index_name ? ( - connector.indexExists && searchIndicesLocator ? ( + connector.indexExists && indexManagementLocator ? ( ) : ( connector.index_name @@ -167,7 +167,8 @@ export const ConnectorsTable: React.FC = ({ 'xpack.enterpriseSearch.content.connectors.connectorTable.columns.actions.viewIndex', { defaultMessage: 'View this connector' } ), - enabled: (connector) => !!connector.index_name, + enabled: (connector) => + !!connector.index_name && (!isCrawler || !!indexManagementLocator), icon: 'eye', isPrimary: false, name: (connector) => @@ -182,13 +183,11 @@ export const ConnectorsTable: React.FC = ({ ), onClick: (connector) => { if (isCrawler) { - // crawler always has an index this is to satisfy TS - if (connector.index_name) { - navigateToUrl( - generateEncodedPath(SEARCH_INDEX_PATH, { - indexName: connector.index_name, - }) - ); + if (connector.index_name && indexManagementLocator) { + indexManagementLocator.navigate({ + indexName: connector.index_name, + page: 'index_details', + }); } } else { navigateToUrl( diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/connector_view_search_indices_details/connector_view_search_indices_details.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/connector_view_search_indices_details/connector_view_search_indices_details.tsx index a21d8a8408811..46bf893c4462a 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/connector_view_search_indices_details/connector_view_search_indices_details.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/connector_view_search_indices_details/connector_view_search_indices_details.tsx @@ -19,8 +19,8 @@ export const ConnectorViewIndexLink: React.FC<{ const { share } = useValues(KibanaLogic); const searchIndexDetailsUrl = share?.url.locators - .get('SEARCH_INDEX_DETAILS_LOCATOR_ID') - ?.useUrl({ indexName }); + .get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID') + ?.useUrl({ indexName, page: 'index_details' }); return searchIndexDetailsUrl ? ( useKibanaReact(); + +const SearchIndexManagementApp: React.FC<{ + indexManagement: IndexManagementPluginSetup; +}> = ({ indexManagement }) => { + const { history, searchNavigation, cloud } = useKibana().services; + const indexManagementRef = useRef(null); + const setBreadcrumbs = useCallback( + (crumbs: ChromeBreadcrumb[] = [], appHistory?: ScopedHistory) => { + const wrapBreadcrumb = (item: ChromeBreadcrumb, scopedHistory: ScopedHistory) => ({ + ...item, + ...(item.href ? reactRouterNavigate(scopedHistory, item.href) : {}), + }); + + const wrapBreadcrumbValue = !cloud?.isServerlessEnabled + ? [wrapBreadcrumb(PARENT_BREADCRUMB, history)] + : []; + + const breadcrumbValue = [ + ...wrapBreadcrumbValue, + ...crumbs.map((item) => wrapBreadcrumb(item, appHistory || history)), + ]; + + searchNavigation?.breadcrumbs.setSearchBreadCrumbs(breadcrumbValue); + }, + [searchNavigation, history, cloud] + ); + + useEffect(() => { + let isMounted = true; + let indexManagementUnmount: (() => void) | undefined; + indexManagement + .renderIndexManagementApp({ + element: indexManagementRef.current, + history, + setBreadcrumbs, + }) + .then((unmount) => { + if (isMounted) { + indexManagementUnmount = unmount; + } else { + // Component already unmounted while the promise was in-flight; + // tear down immediately to avoid leaking the rendered app. + unmount(); + } + }); + + return () => { + isMounted = false; + indexManagementUnmount?.(); + }; + }, [indexManagement, indexManagementRef, setBreadcrumbs, history]); + + return ( + + +
+ + + ); +}; + +export const renderIndexManagementApp = async ( + element: HTMLElement, + deps: { + core: CoreSetup; + history: ScopedHistory; + indexManagement: IndexManagementPluginSetup; + } +) => { + const { core, history, indexManagement } = deps; + const [coreStart, depsStart] = await core.getStartServices(); + const services = { + ...depsStart, + history, + }; + ReactDOM.render( + + + + + + + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/navigation_tree.ts b/x-pack/solutions/search/plugins/enterprise_search/public/navigation_tree.ts index 3a36a66c685e6..c92b7a574c3c5 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/navigation_tree.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/public/navigation_tree.ts @@ -154,19 +154,7 @@ export const getNavigationTreeDefinition = ({ children: [ { children: [ - { - getIsActive: ({ pathNameSerialized, prepend }) => { - return ( - pathNameSerialized.startsWith( - prepend('/app/elasticsearch/index_management/indices') - ) || - pathNameSerialized.startsWith( - prepend('/app/management/data/index_management') - ) - ); - }, - link: 'management:index_management', - }, + { link: 'management:index_management' }, { link: 'management:index_lifecycle_management' }, { link: 'management:snapshot_restore' }, { link: 'management:transform' }, diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/plugin.ts b/x-pack/solutions/search/plugins/enterprise_search/public/plugin.ts index b388c7502e357..b5f31f8ced589 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/public/plugin.ts @@ -25,7 +25,10 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FleetStart } from '@kbn/fleet-plugin/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { i18n } from '@kbn/i18n'; -import type { IndexManagementPluginStart } from '@kbn/index-management-shared-types'; +import type { + IndexManagementPluginSetup, + IndexManagementPluginStart, +} from '@kbn/index-management-shared-types'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { MlPluginStart } from '@kbn/ml-plugin/public'; @@ -46,6 +49,8 @@ import { SEARCH_PRODUCT_NAME, SEARCH_HOMEPAGE, SEARCH_APPS_TITLE, + SEARCH_INDEX_MANAGEMENT_APP_ID, + SEARCH_INDEX_MANAGEMENT_APP_BASE, } from '../common/constants'; import { registerLocators } from '../common/locators'; import type { ClientConfigType, InitialAppData } from '../common/types'; @@ -67,6 +72,7 @@ export type EnterpriseSearchPublicStart = ReturnType { const { - services: { application }, + services: { application, share }, } = useKibana(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -25,6 +24,8 @@ export const AddDataButton: React.FC = () => { setIsPopoverOpen(false); }; + const indexManagementLocator = share?.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); + const items = [ { defaultMessage: 'Browse sample datasets', })} , - { - closePopover(); - application.navigateToApp(SEARCH_INDICES, { path: 'create' }); - }} - > - {i18n.translate('xpack.search.gettingStarted.addDataButton.createEmptyIndex', { - defaultMessage: 'Create an empty index', - })} - , + ...(indexManagementLocator + ? [ + { + closePopover(); + indexManagementLocator.navigate({ + page: 'index_list', + }); + }} + > + {i18n.translate('xpack.search.gettingStarted.addDataButton.createEmptyIndex', { + defaultMessage: 'Create an empty index', + })} + , + ] + : []), ]; const button = ( diff --git a/x-pack/solutions/search/plugins/search_getting_started/test/scout/ui/parallel_tests/getting_started_admin.spec.ts b/x-pack/solutions/search/plugins/search_getting_started/test/scout/ui/parallel_tests/getting_started_admin.spec.ts index 6f3bcf7ecc10a..97700f6c55cb3 100644 --- a/x-pack/solutions/search/plugins/search_getting_started/test/scout/ui/parallel_tests/getting_started_admin.spec.ts +++ b/x-pack/solutions/search/plugins/search_getting_started/test/scout/ui/parallel_tests/getting_started_admin.spec.ts @@ -115,7 +115,7 @@ test.describe( await test.step('navigates to create index page', async () => { await pageObjects.gettingStarted.goto(); await pageObjects.gettingStarted.selectAddDataOption('gettingStartedCreateIndexMenuItem'); - await expect(page).toHaveURL(/indices\/create/); + await expect(page).toHaveURL(/index_management\/indices/); }); }); diff --git a/x-pack/solutions/search/plugins/search_homepage/moon.yml b/x-pack/solutions/search/plugins/search_homepage/moon.yml index cb19e29cfd258..f0934e060c8a3 100644 --- a/x-pack/solutions/search/plugins/search_homepage/moon.yml +++ b/x-pack/solutions/search/plugins/search_homepage/moon.yml @@ -44,7 +44,6 @@ dependsOn: - '@kbn/search-shared-ui' - '@kbn/search-code-examples' - '@kbn/try-in-console' - - '@kbn/deeplinks-search' - '@kbn/licensing-types' - '@kbn/licensing-plugin' - '@kbn/react-query' diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/get_started_with_elasticsearch.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/get_started_with_elasticsearch.tsx index 369b7be4fdd86..91aee3826d198 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/components/get_started_with_elasticsearch.tsx +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/get_started_with_elasticsearch.tsx @@ -6,7 +6,6 @@ */ import { consoleTutorials } from '@kbn/search-code-examples'; import { TryInConsoleButton } from '@kbn/try-in-console'; -import { SEARCH_INDICES, SEARCH_INDICES_CREATE_INDEX } from '@kbn/deeplinks-search/constants'; import { EuiBadge, EuiCard, @@ -100,28 +99,18 @@ const GettingStartedCards: React.FC = ({ }; export const GetStartedWithElasticsearch = () => { - const { - application, - share, - console: consolePlugin, - sampleDataIngest, - chrome, - } = useKibana().services; + const { application, share, console: consolePlugin, sampleDataIngest } = useKibana().services; const [hoveredCard, setHoveredCard] = React.useState(null); const onFileUpload = useCallback(() => { application.navigateToApp('ml', { path: 'filedatavisualizer' }); }, [application]); - const onCreateIndex = useCallback(() => { - const createIndexUrl = chrome?.navLinks.get( - `${SEARCH_INDICES}:${SEARCH_INDICES_CREATE_INDEX}` - )?.url; + const indexManagementLocator = share?.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); - if (createIndexUrl) { - application?.navigateToUrl(createIndexUrl); - } - }, [application, chrome]); + const onCreateIndex = useCallback(() => { + indexManagementLocator?.navigate({ page: 'index_list' }); + }, [indexManagementLocator]); const { hasRequiredLicense, diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/sample_data_action_button.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/sample_data_action_button.tsx index 6c5ffba164c70..ed7ca17eb5d04 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/components/sample_data_action_button.tsx +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/sample_data_action_button.tsx @@ -73,7 +73,7 @@ export const SampleDataActionButton = ({ const navigateToDashboard = useNavigateToDashboard(dashboardId); const navigateToIndexDetails = useCallback(async () => { - const indexDetailsLocator = share.url.locators.get('SEARCH_INDEX_DETAILS_LOCATOR_ID'); + const indexDetailsLocator = share.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); if (indexDetailsLocator && indexName) { await indexDetailsLocator.navigate({ indexName }); } diff --git a/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage/metric_panels.tsx b/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage/metric_panels.tsx index 1a709af2914c5..1eaed0d8d9634 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage/metric_panels.tsx +++ b/x-pack/solutions/search/plugins/search_homepage/public/components/search_homepage/metric_panels.tsx @@ -167,10 +167,11 @@ export const METRIC_PANEL_ITEMS: Array = [ metricTitle: i18n.translate('xpack.searchHomepage.metricPanels.empty.dataManagement.title', { defaultMessage: 'Data Management', }), - onPanelClick: ({ application }) => { - application.navigateToApp('management', { - path: 'data/index_management', - }); + onPanelClick: async ({ share }) => { + const indexManagementLocator = share.url.locators.get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'); + if (indexManagementLocator) { + await indexManagementLocator.navigate({ page: 'index_list' }); + } }, dataTestSubj: 'searchHomepageNavLinks-dataManagement', }, diff --git a/x-pack/solutions/search/plugins/search_homepage/tsconfig.json b/x-pack/solutions/search/plugins/search_homepage/tsconfig.json index 45d7fb34bb09f..cb850741bec2d 100644 --- a/x-pack/solutions/search/plugins/search_homepage/tsconfig.json +++ b/x-pack/solutions/search/plugins/search_homepage/tsconfig.json @@ -38,7 +38,6 @@ "@kbn/search-shared-ui", "@kbn/search-code-examples", "@kbn/try-in-console", - "@kbn/deeplinks-search", "@kbn/licensing-types", "@kbn/licensing-plugin", "@kbn/react-query", diff --git a/x-pack/solutions/search/plugins/search_indices/common/index.ts b/x-pack/solutions/search/plugins/search_indices/common/index.ts index 345f85fb135c3..f58158e225da3 100644 --- a/x-pack/solutions/search/plugins/search_indices/common/index.ts +++ b/x-pack/solutions/search/plugins/search_indices/common/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { SearchIndices, SearchIndexManagement } from '@kbn/deeplinks-search/deep_links'; -import { SEARCH_INDICES, SEARCH_INDEX_MANAGEMENT } from '@kbn/deeplinks-search'; +import type { SearchIndices } from '@kbn/deeplinks-search/deep_links'; +import { SEARCH_INDICES } from '@kbn/deeplinks-search'; export const PLUGIN_ID = 'searchIndices'; export const PLUGIN_NAME = 'searchIndices'; export const INDICES_APP_ID: SearchIndices = SEARCH_INDICES; -export const SEARCH_INDEX_MANAGEMENT_APP_ID: SearchIndexManagement = SEARCH_INDEX_MANAGEMENT; + export type { IndicesStatusResponse, UserStartPrivilegesResponse } from './types'; diff --git a/x-pack/solutions/search/plugins/search_indices/kibana.jsonc b/x-pack/solutions/search/plugins/search_indices/kibana.jsonc index 6343f9ba15ab0..3ae92d157562e 100644 --- a/x-pack/solutions/search/plugins/search_indices/kibana.jsonc +++ b/x-pack/solutions/search/plugins/search_indices/kibana.jsonc @@ -25,10 +25,6 @@ "serverless", "searchNavigation", "sampleDataIngest" - ], - "requiredBundles": [ - "kibanaReact", - "esUiShared" ] } } diff --git a/x-pack/solutions/search/plugins/search_indices/public/plugin.ts b/x-pack/solutions/search/plugins/search_indices/public/plugin.ts index 041306200f01b..c8edf47db4b0b 100644 --- a/x-pack/solutions/search/plugins/search_indices/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_indices/public/plugin.ts @@ -5,14 +5,7 @@ * 2.0. */ -import { - type CoreSetup, - type CoreStart, - type Plugin, - DEFAULT_APP_CATEGORIES, -} from '@kbn/core/public'; -import { SEARCH_INDICES_CREATE_INDEX } from '@kbn/deeplinks-search/constants'; -import { i18n } from '@kbn/i18n'; +import { type CoreSetup, type CoreStart, type Plugin } from '@kbn/core/public'; import type { Subscription } from 'rxjs'; import { docLinks } from '../common/doc_links'; @@ -21,16 +14,8 @@ import type { SearchIndicesAppPluginStartDependencies, SearchIndicesPluginSetup, SearchIndicesPluginStart, - SearchIndicesServicesContextDeps, } from './types'; -import { initQueryClient } from './services/query_client'; -import { SEARCH_INDEX_MANAGEMENT_APP_ID, INDICES_APP_ID } from '../common'; -import { - CREATE_INDEX_PATH, - INDICES_APP_BASE, - SearchIndexDetailsTabValues, - SEARCH_INDEX_MANAGEMENT_APP_BASE, -} from './routes'; + import { registerLocators } from './locators'; export class SearchIndicesPlugin @@ -45,55 +30,6 @@ export class SearchIndicesPlugin ): SearchIndicesPluginSetup { this.pluginEnabled = true; - const queryClient = initQueryClient(core.notifications.toasts); - - core.application.register({ - id: INDICES_APP_ID, - appRoute: INDICES_APP_BASE, - deepLinks: [ - { - id: SEARCH_INDICES_CREATE_INDEX, - path: CREATE_INDEX_PATH, - title: i18n.translate('xpack.searchIndices.elasticsearchIndices.createIndexTitle', { - defaultMessage: 'Create index', - }), - visibleIn: ['globalSearch'], - }, - ], - title: i18n.translate('xpack.searchIndices.elasticsearchIndices.startAppTitle', { - defaultMessage: 'Elasticsearch Indices', - }), - async mount({ element, history }) { - const { renderApp } = await import('./application'); - const { SearchIndicesRouter } = await import('./components/indices_router'); - const [coreStart, depsStart] = await core.getStartServices(); - const startDeps: SearchIndicesServicesContextDeps = { - ...depsStart, - history, - }; - return renderApp(SearchIndicesRouter, coreStart, startDeps, element, queryClient); - }, - visibleIn: [], - }); - core.application.register({ - id: SEARCH_INDEX_MANAGEMENT_APP_ID, - appRoute: SEARCH_INDEX_MANAGEMENT_APP_BASE, - category: DEFAULT_APP_CATEGORIES.enterpriseSearch, - title: i18n.translate('xpack.searchIndices.elasticsearchIndices.indexManagementTitle', { - defaultMessage: 'Index Management', - }), - async mount({ element, history }) { - const { renderIndexManagementApp } = await import('./index_management_application'); - return renderIndexManagementApp(element, { - core, - history, - indexManagement: plugins.indexManagement, - }); - }, - order: 2, - visibleIn: ['sideNav'], - }); - registerLocators(plugins.share); return { @@ -105,26 +41,8 @@ export class SearchIndicesPlugin core: CoreStart, deps: SearchIndicesAppPluginStartDependencies ): SearchIndicesPluginStart { - const { indexManagement } = deps; docLinks.setDocLinks(core.docLinks.links); - if (this.pluginEnabled) { - this.activeSolutionIdSubscription = core.chrome - .getActiveSolutionNavId$() - .subscribe((activeSolutionId) => { - if (activeSolutionId === 'es') { - indexManagement?.extensionsService.setIndexDetailsPageRoute({ - renderRoute: (indexName, detailsTabId) => { - const route = `/app/elasticsearch/indices/index_details/${indexName}`; - if (detailsTabId && SearchIndexDetailsTabValues.includes(detailsTabId)) { - return `${route}/${detailsTabId}`; - } - return route; - }, - }); - } - }); - } return { enabled: this.pluginEnabled, }; diff --git a/x-pack/solutions/search/plugins/search_navigation/kibana.jsonc b/x-pack/solutions/search/plugins/search_navigation/kibana.jsonc index dd148bc5c9f82..11c08f91b6111 100644 --- a/x-pack/solutions/search/plugins/search_navigation/kibana.jsonc +++ b/x-pack/solutions/search/plugins/search_navigation/kibana.jsonc @@ -12,10 +12,12 @@ "xpack", "searchNavigation" ], - "requiredPlugins": [], + "requiredPlugins": ["share"], "optionalPlugins": [ "serverless", - "spaces" + "spaces", + "indexManagement", + "serverless" ], "requiredBundles": [] } diff --git a/x-pack/solutions/search/plugins/search_navigation/moon.yml b/x-pack/solutions/search/plugins/search_navigation/moon.yml index 917fac2514682..cf6889284aae3 100644 --- a/x-pack/solutions/search/plugins/search_navigation/moon.yml +++ b/x-pack/solutions/search/plugins/search_navigation/moon.yml @@ -26,6 +26,8 @@ dependsOn: - '@kbn/core-plugins-browser' - '@kbn/deeplinks-search' - '@kbn/spaces-plugin' + - '@kbn/share-plugin' + - '@kbn/index-management-shared-types' tags: - plugin - prod diff --git a/x-pack/solutions/search/plugins/search_navigation/public/base_classic_navigation_items.tsx b/x-pack/solutions/search/plugins/search_navigation/public/base_classic_navigation_items.tsx index 95a3ee005649c..3077c4aa3c2e3 100644 --- a/x-pack/solutions/search/plugins/search_navigation/public/base_classic_navigation_items.tsx +++ b/x-pack/solutions/search/plugins/search_navigation/public/base_classic_navigation_items.tsx @@ -8,7 +8,11 @@ import React from 'react'; import { EuiText } from '@elastic/eui'; -import { SEARCH_HOMEPAGE, SEARCH_GETTING_STARTED } from '@kbn/deeplinks-search'; +import { + SEARCH_HOMEPAGE, + SEARCH_GETTING_STARTED, + SEARCH_INDEX_MANAGEMENT, +} from '@kbn/deeplinks-search'; import { i18n } from '@kbn/i18n'; import type { ClassicNavItem } from './types'; @@ -56,9 +60,10 @@ export const BaseClassicNavItems: ClassicNavItem[] = [ { 'data-test-subj': 'searchSideNav-Indices', deepLink: { - link: 'elasticsearchIndexManagement', + link: SEARCH_INDEX_MANAGEMENT, + shouldShowActiveForSubroutes: true, }, - id: 'search_indices', + id: 'index_management', }, { 'data-test-subj': 'searchSideNav-Playground', diff --git a/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/index.ts b/x-pack/solutions/search/plugins/search_navigation/public/constants.ts similarity index 51% rename from x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/index.ts rename to x-pack/solutions/search/plugins/search_navigation/public/constants.ts index d2b89daf3ea94..4cd4e01d4494e 100644 --- a/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/index.ts +++ b/x-pack/solutions/search/plugins/search_navigation/public/constants.ts @@ -5,10 +5,4 @@ * 2.0. */ -import type { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('search indices APIs', function () { - loadTestFile(require.resolve('./status')); - }); -} +export const SEARCH_INDEX_MANAGEMENT_LOCATOR_ID = 'SEARCH_INDEX_MANAGEMENT_LOCATOR_ID'; diff --git a/x-pack/solutions/search/plugins/search_navigation/public/index.ts b/x-pack/solutions/search/plugins/search_navigation/public/index.ts index f5fc03e088a2c..ee2322e5a6ba2 100644 --- a/x-pack/solutions/search/plugins/search_navigation/public/index.ts +++ b/x-pack/solutions/search/plugins/search_navigation/public/index.ts @@ -18,3 +18,5 @@ export type { ClassicNavItem, ClassicNavItemDeepLink, } from './types'; + +export { SEARCH_INDEX_MANAGEMENT_LOCATOR_ID } from './constants'; diff --git a/x-pack/solutions/search/plugins/search_navigation/public/locator.ts b/x-pack/solutions/search/plugins/search_navigation/public/locator.ts new file mode 100644 index 0000000000000..6567ac326fac8 --- /dev/null +++ b/x-pack/solutions/search/plugins/search_navigation/public/locator.ts @@ -0,0 +1,64 @@ +/* + * 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 { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; +import type { IndexManagementLocatorParams } from '@kbn/index-management-shared-types'; +import { SEARCH_INDEX_MANAGEMENT as SEARCH_INDEX_MANAGEMENT_APP_ID } from '@kbn/deeplinks-search'; + +import type { SearchIndexManagementLocatorParams } from './types'; +import { SEARCH_INDEX_MANAGEMENT_LOCATOR_ID } from './constants'; +export { SEARCH_INDEX_MANAGEMENT_LOCATOR_ID }; + +export interface SearchIndexManagementLocatorDependencies { + /** Resolves chrome style at runtime (when getLocation is called) */ + getChromeStyle: () => Promise<'classic' | 'project' | undefined>; + /** Platform index management locator; used when chrome style is classic */ + indexManagementLocator?: LocatorPublic; +} + +export class SearchIndexManagementLocatorDefinition + implements LocatorDefinition +{ + public readonly id = SEARCH_INDEX_MANAGEMENT_LOCATOR_ID; + + constructor(private readonly deps: SearchIndexManagementLocatorDependencies) {} + + public readonly getLocation = async (params: SearchIndexManagementLocatorParams) => { + const chromeStyle = await this.deps.getChromeStyle(); + + if (chromeStyle === 'project' && this.deps.indexManagementLocator) { + const location = await this.deps.indexManagementLocator.getLocation(params); + if (location?.app) return location; + } + + switch (params.page) { + case 'index_list': + return { + app: SEARCH_INDEX_MANAGEMENT_APP_ID, + path: '/indices', + state: {}, + }; + case 'index_details': { + let path = `/indices/index_details?indexName=${encodeURIComponent(params.indexName)}`; + if (params.tab) { + path += `&tab=${encodeURIComponent(params.tab)}`; + } + return { + app: SEARCH_INDEX_MANAGEMENT_APP_ID, + path, + state: {}, + }; + } + default: + return { + app: SEARCH_INDEX_MANAGEMENT_APP_ID, + path: '/indices', + state: {}, + }; + } + }; +} diff --git a/x-pack/solutions/search/plugins/search_navigation/public/plugin.ts b/x-pack/solutions/search/plugins/search_navigation/public/plugin.ts index 4f74f3af4e933..00bf3baa26bcf 100644 --- a/x-pack/solutions/search/plugins/search_navigation/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_navigation/public/plugin.ts @@ -12,19 +12,25 @@ import type { PluginInitializerContext, ScopedHistory, } from '@kbn/core/public'; -import type { Subscription } from 'rxjs'; +import { firstValueFrom, type Subscription } from 'rxjs'; import type { ChromeBreadcrumb, ChromeStyle } from '@kbn/core-chrome-browser'; import { i18n } from '@kbn/i18n'; import type { Logger } from '@kbn/logging'; import { SEARCH_HOMEPAGE } from '@kbn/deeplinks-search'; +import { + INDEX_MANAGEMENT_LOCATOR_ID, + type IndexManagementLocatorParams, +} from '@kbn/index-management-shared-types'; import type { SearchNavigationPluginSetup, SearchNavigationPluginStart, ClassicNavItem, SearchNavigationSetBreadcrumbsOptions, AppPluginStartDependencies, + AppPluginSetupDependencies, } from './types'; import { classicNavigationFactory } from './classic_navigation'; +import { SearchIndexManagementLocatorDefinition } from './locator'; export class SearchNavigationPlugin implements Plugin @@ -41,7 +47,20 @@ export class SearchNavigationPlugin this.logger = this.initializerContext.logger.get(); } - public setup(_core: CoreSetup): SearchNavigationPluginSetup { + public setup(core: CoreSetup, plugins: AppPluginSetupDependencies): SearchNavigationPluginSetup { + const indexManagementLocator = plugins.share.url.locators.get( + INDEX_MANAGEMENT_LOCATOR_ID + ); + const getChromeStyle = async (): Promise<'classic' | 'project' | undefined> => { + const [coreStart] = await core.getStartServices(); + return firstValueFrom(coreStart.chrome.getChromeStyle$()); + }; + plugins.share.url.locators.create( + new SearchIndexManagementLocatorDefinition({ + getChromeStyle, + indexManagementLocator: indexManagementLocator ?? undefined, + }) + ); return {}; } diff --git a/x-pack/solutions/search/plugins/search_navigation/public/types.ts b/x-pack/solutions/search/plugins/search_navigation/public/types.ts index 269aeec94bf44..caae941fc95c1 100644 --- a/x-pack/solutions/search/plugins/search_navigation/public/types.ts +++ b/x-pack/solutions/search/plugins/search_navigation/public/types.ts @@ -8,9 +8,11 @@ import type { ReactNode } from 'react'; import type { AppDeepLinkId, ChromeBreadcrumb } from '@kbn/core-chrome-browser'; import type { ScopedHistory } from '@kbn/core/public'; -import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import type { ServerlessPluginStart } from '@kbn/serverless/public'; import type { SolutionNavProps } from '@kbn/shared-ux-page-solution-nav'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; +import type { IndexManagementLocatorParams } from '@kbn/index-management-shared-types'; +import type { SharePluginSetup } from '@kbn/share-plugin/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SearchNavigationPluginSetup {} @@ -30,7 +32,7 @@ export interface SearchNavigationPluginStart { } export interface AppPluginSetupDependencies { - serverless?: ServerlessPluginSetup; + share: SharePluginSetup; } export interface AppPluginStartDependencies { @@ -58,3 +60,8 @@ export interface SearchNavigationSetBreadcrumbsOptions { // need to explicitly set the page breadcrumbs for classic solution view. forClassicChromeStyle?: boolean; } + +/** Subset of IndexManagementLocatorParams used by the Search solution's index management locator */ +export type SearchIndexManagementLocatorParams = + | Extract + | Extract; diff --git a/x-pack/solutions/search/plugins/search_navigation/tsconfig.json b/x-pack/solutions/search/plugins/search_navigation/tsconfig.json index 1eb3f0ef7f885..82bfefbbfe48d 100644 --- a/x-pack/solutions/search/plugins/search_navigation/tsconfig.json +++ b/x-pack/solutions/search/plugins/search_navigation/tsconfig.json @@ -17,6 +17,8 @@ "@kbn/core-plugins-browser", "@kbn/deeplinks-search", "@kbn/spaces-plugin", + "@kbn/share-plugin", + "@kbn/index-management-shared-types", ], "exclude": ["target/**/*"] } diff --git a/x-pack/solutions/search/plugins/search_playground/moon.yml b/x-pack/solutions/search/plugins/search_playground/moon.yml index 4deec66c6d3f7..10aa0f37e3b24 100644 --- a/x-pack/solutions/search/plugins/search_playground/moon.yml +++ b/x-pack/solutions/search/plugins/search_playground/moon.yml @@ -56,7 +56,6 @@ dependsOn: - '@kbn/alerts-ui-shared' - '@kbn/ui-actions-plugin' - '@kbn/file-upload-common' - - '@kbn/deeplinks-search' - '@kbn/logging-mocks' - '@kbn/inference-plugin' - '@kbn/code-editor' diff --git a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx index 3f0ad2ab03d7c..45cc31a424ef7 100644 --- a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx +++ b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx @@ -7,24 +7,14 @@ import type { FC, PropsWithChildren } from 'react'; import React from 'react'; -import { render, fireEvent, waitFor } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { useKibana } from '../../hooks/use_kibana'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { CreateIndexButton } from './create_index_button'; -// Mocking the useKibana hook jest.mock('../../hooks/use_kibana', () => ({ useKibana: jest.fn(() => ({ - services: { - application: { - navigateToUrl: jest.fn(), - }, - chrome: { - navLinks: { - get: jest.fn().mockReturnValue(undefined), - }, - }, - }, + services: {}, })), })); @@ -36,6 +26,16 @@ const Wrapper: FC> = ({ children }) => { ); }; +const mockShareWithUrl = (url: string | undefined) => ({ + url: { + locators: { + get: jest.fn().mockReturnValue({ + useUrl: jest.fn().mockReturnValue(url), + }), + }, + }, +}); + describe('CreateIndexButton', () => { it('renders correctly when there is no link to indices', async () => { const { queryByTestId } = render(, { wrapper: Wrapper }); @@ -44,20 +44,9 @@ describe('CreateIndexButton', () => { }); it('renders correctly when navlink exists', async () => { - const navigateToUrl = jest.fn(); - (useKibana as unknown as jest.Mock).mockImplementation(() => ({ services: { - application: { - navigateToUrl, - }, - chrome: { - navLinks: { - get: jest.fn().mockReturnValue({ - url: 'mock-url', - }), - }, - }, + share: mockShareWithUrl('mock-url'), }, })); @@ -65,11 +54,6 @@ describe('CreateIndexButton', () => { const createIndexButton = getByTestId('createIndexButton'); expect(createIndexButton).toBeInTheDocument(); - - fireEvent.click(createIndexButton); - - await waitFor(() => { - expect(navigateToUrl).toHaveBeenCalledWith('mock-url'); - }); + expect(createIndexButton).toHaveAttribute('href', 'mock-url'); }); }); diff --git a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx index f6d3e3035d3f5..c75413cd87326 100644 --- a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx +++ b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx @@ -5,42 +5,26 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React from 'react'; import { EuiButton, EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SEARCH_INDICES, SEARCH_INDICES_CREATE_INDEX } from '@kbn/deeplinks-search/constants'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../../hooks/use_kibana'; export const CreateIndexButton: React.FC = () => { const { - services: { application, chrome }, + services: { share }, } = useKibana(); - const createIndexUrl = chrome.navLinks.get( - `${SEARCH_INDICES}:${SEARCH_INDICES_CREATE_INDEX}` - )?.url; + const searchIndexDetailsUrl = share?.url.locators + .get('SEARCH_INDEX_MANAGEMENT_LOCATOR_ID') + ?.useUrl({ page: 'index_list' }); - const handleCreateIndexClick = useCallback( - (event: React.MouseEvent) => { - event.preventDefault(); - - if (!createIndexUrl) { - return; - } - - application?.navigateToUrl(createIndexUrl); - }, - [application, createIndexUrl] - ); - - return createIndexUrl ? ( - // eslint-disable-next-line @elastic/eui/href-or-on-click + return searchIndexDetailsUrl ? ( { - return ( - pathNameSerialized.startsWith( - prepend('/app/elasticsearch/index_management/indices') - ) || - pathNameSerialized.startsWith(prepend('/app/management/data/index_management')) - ); - }, - link: 'management:index_management', - breadcrumbStatus: 'hidden', - }, + { link: 'management:index_management', breadcrumbStatus: 'hidden' }, { link: 'management:index_lifecycle_management', breadcrumbStatus: 'hidden' }, { link: 'management:snapshot_restore', breadcrumbStatus: 'hidden' }, { link: 'management:transform', breadcrumbStatus: 'hidden' }, diff --git a/x-pack/solutions/search/test/functional_search/apps/search_playground/playground_overview.ess.ts b/x-pack/solutions/search/test/functional_search/apps/search_playground/playground_overview.ess.ts index 6ecb33f630244..0a31a1c3f2dd7 100644 --- a/x-pack/solutions/search/test/functional_search/apps/search_playground/playground_overview.ess.ts +++ b/x-pack/solutions/search/test/functional_search/apps/search_playground/playground_overview.ess.ts @@ -36,7 +36,7 @@ const esArchiveIndex = export default function (ftrContext: FtrProviderContext) { const { getService, getPageObjects } = ftrContext; - const pageObjects = getPageObjects(['common', 'searchPlayground']); + const pageObjects = getPageObjects(['common', 'searchPlayground', 'indexManagement']); const commonAPI = MachineLearningCommonAPIProvider(ftrContext); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); @@ -189,11 +189,10 @@ export default function (ftrContext: FtrProviderContext) { it('should be able to create index from UI', async () => { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToExists(); await pageObjects.searchPlayground.PlaygroundStartChatPage.clickCreateIndex(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToBeOnCreateIndexPage(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.setIndexNameValue(indexName); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToBeEnabled(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.clickCreateIndexButton(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToBeOnIndexDetailsPage(); + await pageObjects.indexManagement.expectToBeOnIndexManagement(); + await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.indexManagement.setCreateIndexName(indexName); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); // add mapping await es.indices.putMapping({ diff --git a/x-pack/solutions/search/test/functional_search/page_objects/search_navigation.ts b/x-pack/solutions/search/test/functional_search/page_objects/search_navigation.ts index 2a63f18032178..f299b7600a501 100644 --- a/x-pack/solutions/search/test/functional_search/page_objects/search_navigation.ts +++ b/x-pack/solutions/search/test/functional_search/page_objects/search_navigation.ts @@ -17,14 +17,6 @@ export function SearchNavigationProvider({ getService, getPageObjects }: FtrProv const testSubjects = getService('testSubjects'); return { - async navigateToElasticsearchOverviewPage(basePath?: string) { - await retry.tryForTime(60 * 1000, async () => { - await common.navigateToApp('enterpriseSearch', { - shouldLoginIfPrompted: false, - basePath, - }); - }); - }, async navigateToElasticsearchSearchHomePage(basePath?: string) { await retry.tryForTime(60 * 1000, async () => { await common.navigateToApp('searchHomepage', { @@ -54,7 +46,7 @@ export function SearchNavigationProvider({ getService, getPageObjects }: FtrProv return; } } - await testSubjects.existOrFail('searchIndicesDetailsPage', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsContent', { timeout: 2000 }); }, async navigateToInferenceManagementPage(expectRedirect: boolean = false) { await common.navigateToApp('searchInferenceEndpoints', { diff --git a/x-pack/solutions/search/test/functional_search/tests/index_management.ts b/x-pack/solutions/search/test/functional_search/tests/index_management.ts index 6ca4dac175f53..7e18559f144a0 100644 --- a/x-pack/solutions/search/test/functional_search/tests/index_management.ts +++ b/x-pack/solutions/search/test/functional_search/tests/index_management.ts @@ -54,7 +54,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('navigates to overview tab', async () => { await pageObjects.indexManagement.changeManageIndexTab('showOverviewIndexMenuButton'); await pageObjects.searchIndexDetailsPage.expectIndexDetailPageHeader(); - await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('data'); + await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('overview'); }); it('navigates to settings tab', async () => { @@ -76,7 +76,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ); await pageObjects.searchIndexDetailsPage.expectIndexDetailPageHeader(); await pageObjects.searchIndexDetailsPage.expectSearchIndexDetailsTabsExists(); - await pageObjects.searchIndexDetailsPage.expectAPIReferenceDocLinkExists(); }); }); }); 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 047e361cabdbe..737d4b1ffa4fd 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 @@ -24,11 +24,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'solutionNavigation', ]); const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - const spaces = getService('spaces'); const searchSpace = getService('searchSpace'); const esDeleteAllIndices = getService('esDeleteAllIndices'); - const retry = getService('retry'); const createIndices = async () => { await esArchiver.load(archivedBooksIndex); @@ -43,7 +40,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }; const indexWithDataName = 'search-books'; const indexWithoutDataName = 'search-empty-index'; - const indexWithDenseVectorName = 'search-national-parks'; const indexDoesNotExistName = 'search-not-found'; describe('Search index details page', function () { @@ -75,17 +71,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('can load index detail page', async () => { await pageObjects.searchIndexDetailsPage.expectIndexDetailPageHeader(); await pageObjects.searchIndexDetailsPage.expectSearchIndexDetailsTabsExists(); - await pageObjects.searchIndexDetailsPage.expectAPIReferenceDocLinkExists(); - await pageObjects.searchIndexDetailsPage.expectAPIReferenceDocLinkMissingInMoreOptions(); }); it('should have embedded dev console', async () => { await testHasEmbeddedConsole(pageObjects); }); it('should have breadcrumbs', async () => { - await pageObjects.searchIndexDetailsPage.expectIndexNametoBeInBreadcrumbs( - indexWithoutDataName - ); - await pageObjects.searchIndexDetailsPage.expectBreadcrumbsToBeAvailable('Build'); await pageObjects.searchIndexDetailsPage.expectBreadcrumbsToBeAvailable( 'Index Management' ); @@ -100,150 +90,107 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.searchNavigation.navigateToIndexDetailPage(indexWithoutDataName); }); - it('should have connection details', async () => { + it('should have connection details button', async () => { await pageObjects.searchIndexDetailsPage.expectConnectionDetails(); }); - - it('should have basic example texts', async () => { - await pageObjects.searchIndexDetailsPage.expectHasSampleDocuments(); + it('should open connection details flyout', async () => { + await pageObjects.searchIndexDetailsPage.expectConnectionDetailsFlyoutToOpen(); + await pageObjects.searchIndexDetailsPage.closeConnectionDetailsFlyout(); }); - it('should have quick stats', async () => { - await pageObjects.searchIndexDetailsPage.expectQuickStats(); + it('should have storage quick stats', async () => { + await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveIndexStorage(); + }); + it('should have status quick stats with open status and health', async () => { await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveIndexStatus(); - await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveIndexStorage('227.00 B'); - await pageObjects.searchIndexDetailsPage.expectQuickStatsAIMappings(); + await pageObjects.searchIndexDetailsPage.expectStatusDetailsToShowStatus('Open'); + await pageObjects.searchIndexDetailsPage.expectStatusDetailsToShowHealthBadge(); + }); + it('should show zero documents in status details for empty index', async () => { + await pageObjects.searchIndexDetailsPage.expectStatusDetailsToHaveDocCount(0); }); - it('should show code examples for adding documents', async () => { - await pageObjects.searchIndexDetailsPage.expectAddDocumentCodeExamples(); - await pageObjects.searchIndexDetailsPage.expectSelectedLanguage('python'); - await pageObjects.searchIndexDetailsPage.codeSampleContainsValue( - 'installCodeExample', - 'pip install' - ); - await pageObjects.searchIndexDetailsPage.selectCodingLanguage('javascript'); - await pageObjects.searchIndexDetailsPage.codeSampleContainsValue( - 'installCodeExample', - 'npm install' - ); - await pageObjects.searchIndexDetailsPage.selectCodingLanguage('curl'); - await pageObjects.searchIndexDetailsPage.openConsoleCodeExample(); - await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen(); - await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar(); + it('should have add data section', async () => { + await pageObjects.searchIndexDetailsPage.expectAddDataSectionExists(); + }); + it('should not show data preview for empty index', async () => { + await pageObjects.searchIndexDetailsPage.expectDataPreviewNotExists(); }); describe('With data', () => { before(async () => { await pageObjects.searchNavigation.navigateToIndexDetailPage(indexWithDataName); }); - it('should have index documents', async () => { - await pageObjects.searchIndexDetailsPage.expectHasIndexDocuments(); + it('should show open status and health in status details', async () => { + await pageObjects.searchIndexDetailsPage.expectStatusDetailsToShowStatus('Open'); + await pageObjects.searchIndexDetailsPage.expectStatusDetailsToShowHealthBadge(); }); - it('menu action item should be replaced with playground', async () => { - await pageObjects.searchIndexDetailsPage.expectActionItemReplacedWhenHasDocs(); + it('should have documents in status details', async () => { + await pageObjects.searchIndexDetailsPage.expectStatusDetailsToHaveDocCount(46); }); - it('should have link to API reference doc link in options menu', async () => { - await pageObjects.searchIndexDetailsPage.clickMoreOptionsActionsButton(); - await pageObjects.searchIndexDetailsPage.expectAPIReferenceDocLinkExistsInMoreOptions(); + it('should show data preview', async () => { + await pageObjects.searchIndexDetailsPage.expectDataPreviewExists(); }); - it('should have documents in quick stats', async () => { - await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveDocumentCount(46); - }); - it('should have with data tabs', async () => { + it('should have overview, mappings, and settings tabs', async () => { await pageObjects.searchIndexDetailsPage.expectTabsExists(); - await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('data'); }); it('should be able to change tabs to mappings and mappings is shown', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('mappingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-mappings'); await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('mappings'); await pageObjects.searchIndexDetailsPage.expectMappingsComponentIsVisible(); }); it('should be able to change tabs to settings and settings is shown', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('settingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-settings'); await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('settings'); await pageObjects.searchIndexDetailsPage.expectSettingsComponentIsVisible(); }); - it('should be able to delete document', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('dataTab'); - await pageObjects.searchIndexDetailsPage.clickFirstDocumentDeleteAction(); - - // re-open page to refresh queries for test (these will auto-refresh, - // but waiting for that will make this test flakey) - await browser.refresh(); - await retry.tryWithRetries( - 'Wait for document count to update', - async () => { - await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveDocumentCount(45); - }, - { - retryCount: 5, - retryDelay: 1000, - }, - async () => { - await browser.refresh(); - } - ); - }); - }); - describe('With dense vectors', () => { - it('should have ai quick stats for index with semantic mappings', async () => { - await pageObjects.searchNavigation.navigateToIndexDetailPage(indexWithDenseVectorName); - await pageObjects.searchIndexDetailsPage.expectQuickStatsAIMappingsToHaveVectorFields(); - }); }); - // FLAKY: https://github.com/elastic/kibana/issues/248780 - describe.skip('has index actions enabled', () => { + + describe('has index actions enabled', () => { beforeEach(async () => { - await pageObjects.searchNavigation.navigateToIndexDetailPage(indexWithDenseVectorName); + await pageObjects.searchNavigation.navigateToIndexDetailPage(indexWithDataName); }); - it('delete document button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.expectDeleteDocumentActionToBeEnabled(); - }); it('add field button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('mappingsTab'); - await pageObjects.searchIndexDetailsPage.dismissIngestTourIfShown(); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-mappings'); await pageObjects.searchIndexDetailsPage.expectAddFieldToBeEnabled(); }); it('edit settings button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('settingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-settings'); await pageObjects.searchIndexDetailsPage.expectEditSettingsToBeEnabled(); }); - it('delete index button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.expectMoreOptionsActionButtonExists(); - await pageObjects.searchIndexDetailsPage.clickMoreOptionsActionsButton(); - await pageObjects.searchIndexDetailsPage.expectMoreOptionsOverviewMenuIsShown(); - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExistsInMoreOptions(); - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonToBeEnabled(); + it('delete index button exists', async () => { + await pageObjects.searchIndexDetailsPage.expectManageIndexButtonExists(); + await pageObjects.searchIndexDetailsPage.clickManageIndexButton(); + await pageObjects.searchIndexDetailsPage.expectManageIndexContextMenuIsShown(); + await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExists(); }); }); describe('page loading error', () => { before(async () => { - // manually navigate to index detail page for an index that doesn't exist - await browser.navigateTo( - `${spaces.getRootUrl( - spaceCreated.id - )}/app/elasticsearch/indices/index_details/${indexDoesNotExistName}/data` - ); + await pageObjects.common.navigateToApp('indexManagement', { + path: 'indices/index_details', + search: `indexName=${indexDoesNotExistName}`, + basePath: `s/${spaceCreated.id}`, + shouldLoginIfPrompted: false, + }); }); it('has page load error section', async () => { await pageObjects.searchIndexDetailsPage.expectPageLoadErrorExists(); - await pageObjects.searchIndexDetailsPage.expectIndexNotFoundErrorExists(); }); }); - describe('Index more options menu', () => { + describe('Index manage menu', () => { before(async () => { await pageObjects.searchNavigation.navigateToIndexDetailPage(indexWithDataName); }); - it('shows action menu in actions popover', async () => { - await pageObjects.searchIndexDetailsPage.expectMoreOptionsActionButtonExists(); - await pageObjects.searchIndexDetailsPage.clickMoreOptionsActionsButton(); - await pageObjects.searchIndexDetailsPage.expectMoreOptionsOverviewMenuIsShown(); + it('shows action menu in manage index popover', async () => { + await pageObjects.searchIndexDetailsPage.expectManageIndexButtonExists(); + await pageObjects.searchIndexDetailsPage.clickManageIndexButton(); + await pageObjects.searchIndexDetailsPage.expectManageIndexContextMenuIsShown(); }); it('should delete index', async () => { - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExistsInMoreOptions(); + await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExists(); await pageObjects.searchIndexDetailsPage.clickDeleteIndexButton(); await pageObjects.searchIndexDetailsPage.clickConfirmingDeleteIndex(); }); diff --git a/x-pack/solutions/search/test/page_objects/index.ts b/x-pack/solutions/search/test/page_objects/index.ts index 7844e635b5dfd..a818709402d1f 100644 --- a/x-pack/solutions/search/test/page_objects/index.ts +++ b/x-pack/solutions/search/test/page_objects/index.ts @@ -6,14 +6,12 @@ */ import { SearchInferenceManagementPageProvider } from './inference_management_page'; -import { SearchHomePageProvider } from './search_homepage'; import { SearchIndexDetailPageProvider } from './search_index_details_page'; import { SearchPlaygroundPageProvider } from './search_playground_page'; import { SearchQueryRulesPageProvider } from './search_query_rules_page'; import { SearchSynonymsPageProvider } from './search_synonyms_page'; export const searchSharedPageObjects = { - searchHomePage: SearchHomePageProvider, searchIndexDetailsPage: SearchIndexDetailPageProvider, searchInferenceManagementPage: SearchInferenceManagementPageProvider, searchPlayground: SearchPlaygroundPageProvider, diff --git a/x-pack/solutions/search/test/page_objects/search_homepage.ts b/x-pack/solutions/search/test/page_objects/search_homepage.ts deleted file mode 100644 index e3f4446581bfc..0000000000000 --- a/x-pack/solutions/search/test/page_objects/search_homepage.ts +++ /dev/null @@ -1,121 +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 expect from '@kbn/expect'; -import type { FtrProviderContext } from './ftr_provider_context'; - -export function SearchHomePageProvider({ getService }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - const browser = getService('browser'); - - return { - async expectSearchHomePageIsLoaded() { - await testSubjects.existOrFail('search-homepage', { timeout: 2000 }); - }, - - async expectToBeOnHomepage() { - expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/home'); - }, - async expectToNotBeOnHomepage() { - expect(await browser.getCurrentUrl()).not.contain('/app/elasticsearch/home'); - }, - async expectHomepageHeader() { - await testSubjects.existOrFail('search-homepage-header', { timeout: 2000 }); - }, - async expectConsoleLinkExists() { - await testSubjects.existOrFail('searchHomepageEmbeddedConsoleButton'); - }, - async clickConsoleLink() { - await testSubjects.click('searchHomepageEmbeddedConsoleButton'); - }, - async expectEndpointsLinkExists() { - await testSubjects.existOrFail('searchHomepageEndpointsHeaderActionEndpointsApiKeysButton'); - }, - async clickEndpointsLink() { - await testSubjects.click('searchHomepageEndpointsHeaderActionEndpointsApiKeysButton'); - }, - async expectConnectionDetailsFlyoutToBeOpen() { - await testSubjects.existOrFail('connectionDetailsModalTitle'); - }, - async closeConnectionDetailsFlyout() { - await testSubjects.existOrFail('euiFlyoutCloseButton'); - await testSubjects.click('euiFlyoutCloseButton'); - }, - async expectEndpointsTabIsAvailable() { - await testSubjects.existOrFail('connectionDetailsTabBtn-endpoints'); - await testSubjects.click('connectionDetailsTabBtn-endpoints'); - await testSubjects.existOrFail('connectionDetailsEsUrl'); - await testSubjects.existOrFail('connectionDetailsCloudIdSwitch'); - }, - async clickManageApiKeysLink() { - await testSubjects.existOrFail('manageApiKeysButton'); - await testSubjects.click('manageApiKeysButton'); - }, - async expectToBeOnManageApiKeysPage() { - expect(await browser.getCurrentUrl()).contain('/app/management/security/api_keys'); - }, - async expectToBeOnUploadDataPage() { - expect(await browser.getCurrentUrl()).contain('ml/filedatavisualizer'); - }, - async expectToBeOnCustomerEngineerPage() { - expect(await browser.getCurrentUrl()).contain('contact/ce-help'); - }, - async expectToBeOnCreateIndexPage() { - expect(await browser.getCurrentUrl()).contain('app/elasticsearch/indices/create'); - }, - async expectToBeOnObservabilityPage() { - expect(await browser.getCurrentUrl()).contain('manage-data/ingest'); - }, - async expectToBeOnIngestDataToSecurityPage() { - expect(await browser.getCurrentUrl()).contain( - 'security/get-started/ingest-data-to-elastic-security' - ); - }, - async expectToBeOnInstallElasticDefendPage() { - expect(await browser.getCurrentUrl()).contain( - 'security/configure-elastic-defend/install-elastic-defend' - ); - }, - async expectToBeOnCloudSecurityPosturePage() { - expect(await browser.getCurrentUrl()).contain( - 'security/cloud/cloud-security-posture-management' - ); - }, - async expectToBeOnSpacesCreatePage() { - expect(await browser.getCurrentUrl()).contain('observability/start'); - }, - async expectToBeOnSearchLabsPage() { - expect(await browser.getCurrentUrl()).contain('search-labs'); - }, - async expectToBeOnNotebooksExamplesPage() { - expect(await browser.getCurrentUrl()).contain('search-labs/tutorials/examples'); - }, - async expectToBeOnGetStartedDocumentationPage() { - expect(await browser.getCurrentUrl()).contain('docs/solutions/search/get-started'); - }, - async expectToBeOnCommunityPage() { - expect(await browser.getCurrentUrl()).contain('community/'); - }, - async expectToBeOnGiveFeedbackPage() { - expect(await browser.getCurrentUrl()).contain('kibana/feedback'); - }, - async createApiKeyInFlyout(keyName: string) { - await testSubjects.existOrFail('connectionDetailsApiKeyNameInput'); - await testSubjects.existOrFail('connectionDetailsApiKeySubmitBtn'); - await testSubjects.setValue('connectionDetailsApiKeyNameInput', keyName); - await testSubjects.click('connectionDetailsApiKeySubmitBtn'); - await testSubjects.existOrFail('connectionDetailsApiKeySuccessForm'); - await testSubjects.existOrFail('connectionDetailsApiKeyValueRow'); - expect(await testSubjects.getVisibleText('connectionDetailsApiKeySuccessForm')).contain( - keyName - ); - }, - async expectAIAssistantToExist() { - await testSubjects.existOrFail('AiAssistantAppNavControlButton'); - }, - }; -} 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 522c2e21b39ba..1fa5446b08689 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 @@ -14,214 +14,129 @@ export function SearchIndexDetailPageProvider({ getService }: FtrProviderContext const retry = getService('retry'); const expectIndexDetailPageHeader = async function () { - await testSubjects.existOrFail('searchIndexDetailsHeader', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsHeader', { timeout: 2000 }); }; const expectSearchIndexDetailsTabsExists = async function () { - await testSubjects.existOrFail('dataTab'); - await testSubjects.existOrFail('mappingsTab'); - await testSubjects.existOrFail('settingsTab'); + await testSubjects.existOrFail('indexDetailsTab-overview'); + await testSubjects.existOrFail('indexDetailsTab-mappings'); + await testSubjects.existOrFail('indexDetailsTab-settings'); }; return { expectIndexDetailPageHeader, expectSearchIndexDetailsTabsExists, - async expectAPIReferenceDocLinkExists() { - await testSubjects.existOrFail('ApiReferenceDoc', { timeout: 2000 }); - }, async expectIndexDetailsPageIsLoaded() { await expectIndexDetailPageHeader(); await expectSearchIndexDetailsTabsExists(); }, - async expectActionItemReplacedWhenHasDocs() { - await testSubjects.missingOrFail('ApiReferenceDoc', { timeout: 2000 }); - await testSubjects.existOrFail('useInPlaygroundLink', { timeout: 5000 }); - await testSubjects.existOrFail('viewInDiscoverLink', { timeout: 5000 }); - }, async expectConnectionDetails() { - await testSubjects.existOrFail('connectionDetailsEndpoint', { timeout: 2000 }); - expect(await (await testSubjects.find('connectionDetailsEndpoint')).getVisibleText()).match( - /^https?\:\/\/.*(\:\d+)?/ - ); - }, - async expectQuickStats(counts: { total: number; deleted: number } = { total: 0, deleted: 0 }) { - await testSubjects.existOrFail('quickStats', { timeout: 2000 }); - const quickStatsElem = await testSubjects.find('quickStats'); - const quickStatsDocumentElem = await quickStatsElem.findByTestSubject( - 'QuickStatsDocumentCount' - ); - const documentCountVisibileText = await quickStatsDocumentElem.getVisibleText(); - expect(documentCountVisibileText).to.contain(`Document count\n${counts.total}`); - expect(documentCountVisibileText).not.to.contain(`Total\n${counts.total}`); - await testSubjects.click('QuickStatsDocumentCount'); - await retry.tryWithRetries( - 'Wait for redirect to start page', - async () => { - expect(await testSubjects.getVisibleText('QuickStatsDocumentCount')).to.contain( - `Total\n${counts.total}\nDeleted\n${counts.deleted}` - ); - }, - { - retryCount: 2, - retryDelay: 1000, - } - ); + await testSubjects.existOrFail('openConnectionDetails', { timeout: 2000 }); }, - - async expectStatelessQuickStats() { - await testSubjects.existOrFail('quickStats', { timeout: 2000 }); - const quickStatsElem = await testSubjects.find('quickStats'); - const quickStatsDocumentElem = await quickStatsElem.findByTestSubject( - 'QuickStatsDocumentCount' - ); - expect(await quickStatsDocumentElem.getVisibleText()).to.contain('Document count\n0'); - expect(await quickStatsDocumentElem.getVisibleText()).not.to.contain('Index Size\n0.00 B'); - await quickStatsDocumentElem.click(); - expect(await quickStatsDocumentElem.getVisibleText()).to.contain('Index Size\n0.00 B'); + async expectConnectionDetailsFlyoutToOpen() { + await testSubjects.click('openConnectionDetails'); + await testSubjects.existOrFail('connectionDetailsModalBody', { timeout: 5000 }); + await testSubjects.existOrFail('connectionDetailsModalTitle', { timeout: 2000 }); }, - - async expectQuickStatsToHaveIndexStatus() { - await testSubjects.existOrFail('QuickStatsIndexStatus'); + async closeConnectionDetailsFlyout() { + await testSubjects.click('euiFlyoutCloseButton'); + await testSubjects.missingOrFail('connectionDetailsModalBody', { timeout: 2000 }); }, - async expectQuickStatsToHaveIndexStorage(size?: string) { - await testSubjects.existOrFail('QuickStatsStorage'); - if (!size) return; - - const quickStatsElem = await testSubjects.find('quickStats'); - const quickStatsStorageElem = await quickStatsElem.findByTestSubject('QuickStatsStorage'); - expect(await quickStatsStorageElem.getVisibleText()).to.contain(`Storage\n${size}`); + async expectSizeDocCountQuickStats() { + await testSubjects.existOrFail('indexDetailsSizeDocCount', { timeout: 2000 }); }, - - async expectQuickStatsToHaveDocumentCount(count: number) { - await testSubjects.existOrFail('QuickStatsDocumentCount'); - const quickStatsElem = await testSubjects.find('quickStats'); - const quickStatsDocumentElem = await quickStatsElem.findByTestSubject( - 'QuickStatsDocumentCount' - ); - expect(await quickStatsDocumentElem.getVisibleText()).to.contain(`Document count\n${count}`); + async expectSizeDocCountQuickStatsMissing() { + await testSubjects.missingOrFail('indexDetailsSizeDocCount', { timeout: 2000 }); }, - - async expectQuickStatsAIMappings() { - await testSubjects.existOrFail('quickStats', { timeout: 2000 }); - await testSubjects.existOrFail('QuickStatsAIMappings'); - const quickStatsElem = await testSubjects.find('quickStats'); - const quickStatsAIMappingsElem = await quickStatsElem.findByTestSubject( - 'QuickStatsAIMappings' - ); - await quickStatsAIMappingsElem.click(); - await testSubjects.existOrFail('setupAISearchButton', { timeout: 2000 }); + async expectSizeDocCountToHaveDocumentCount(count: number) { + await testSubjects.existOrFail('indexDetailsSizeDocCount', { timeout: 2000 }); + const cardElem = await testSubjects.find('indexDetailsSizeDocCount'); + const visibleText = await cardElem.getVisibleText(); + const label = count === 1 ? 'Document' : 'Documents'; + expect(visibleText).to.contain(`${count}\n${label}`); }, - async expectQuickStatsAIMappingsToHaveSemanticFields() { - const quickStatsDocumentElem = await testSubjects.find('QuickStatsAIMappings'); - await quickStatsDocumentElem.click(); - expect(await quickStatsDocumentElem.getVisibleText()).to.contain('AI Search\n2 Fields'); - await testSubjects.missingOrFail('setupAISearchButton', { timeout: 2000 }); + async expectQuickStatsToHaveIndexStatus() { + await testSubjects.existOrFail('indexDetailsStatus'); }, - - async expectQuickStatsAIMappingsToHaveVectorFields() { - const quickStatsDocumentElem = await testSubjects.find('QuickStatsAIMappings'); - await quickStatsDocumentElem.click(); - expect(await quickStatsDocumentElem.getVisibleText()).to.contain('AI Search\n1 Field'); - await testSubjects.missingOrFail('setupAISearchButton', { timeout: 2000 }); + async expectStatusDetailsToShowStatus(status: 'Open' | 'Closed') { + await testSubjects.existOrFail('indexDetailsStatus', { timeout: 2000 }); + const statusElem = await testSubjects.find('indexDetailsStatus'); + expect(await statusElem.getVisibleText()).to.contain(status); }, - - async expectAddDocumentCodeExamples() { - await testSubjects.existOrFail('SearchIndicesAddDocumentsCode', { timeout: 2000 }); + async expectStatusDetailsToShowHealthBadge() { + await testSubjects.existOrFail('indexDetailsHealthBadge', { timeout: 2000 }); }, - - async expectHasIndexDocuments() { + async expectStatusDetailsToHaveDocCount(count: number) { + const docCountElem = await testSubjects.find('indexDetailsStatusDocCount', 2000); await retry.try(async () => { - await testSubjects.existOrFail('search-index-documents-result', { timeout: 2000 }); + expect(await docCountElem.getVisibleText()).to.contain(`${count}`); }); }, - async expectMoreOptionsActionButtonExists() { - await testSubjects.existOrFail('moreOptionsActionButton'); - }, - async clickMoreOptionsActionsButton() { - await testSubjects.click('moreOptionsActionButton'); - }, - async expectMoreOptionsOverviewMenuIsShown() { - await testSubjects.existOrFail('moreOptionsContextMenu'); - }, - async expectToNavigateToPlayground(indexName: string) { - await testSubjects.click('moreOptionsPlayground'); - expect(await browser.getCurrentUrl()).contain( - `/search_playground/chat?default-index=${indexName}` - ); - await testSubjects.existOrFail('chatPage'); - }, - async expectAPIReferenceDocLinkExistsInMoreOptions() { - await testSubjects.existOrFail('moreOptionsApiReference', { timeout: 2000 }); + async expectQuickStatsToHaveIndexStorage(size?: string) { + await testSubjects.existOrFail('indexDetailsStorage'); + if (!size) return; + + const storageElem = await testSubjects.find('indexDetailsStorage'); + expect(await storageElem.getVisibleText()).to.contain(size); }, - async expectAPIReferenceDocLinkMissingInMoreOptions() { - await testSubjects.missingOrFail('moreOptionsApiReference', { timeout: 2000 }); + + async expectManageIndexButtonExists() { + await testSubjects.existOrFail('indexActionsContextMenuButton'); }, - async expectDeleteIndexButtonToBeDisabled() { - await testSubjects.existOrFail('moreOptionsDeleteIndex'); - const deleteIndexButton = await testSubjects.isEnabled('moreOptionsDeleteIndex'); - expect(deleteIndexButton).to.be(false); - await testSubjects.moveMouseTo('moreOptionsDeleteIndex'); - await testSubjects.existOrFail('moreOptionsDeleteIndexTooltip'); + async clickManageIndexButton() { + await testSubjects.click('indexActionsContextMenuButton'); }, - async expectDeleteIndexButtonToBeEnabled() { - await testSubjects.existOrFail('moreOptionsDeleteIndex'); - const deleteIndexButton = await testSubjects.isEnabled('moreOptionsDeleteIndex'); - expect(deleteIndexButton).to.be(true); + async expectManageIndexContextMenuIsShown() { + await testSubjects.existOrFail('indexContextMenu'); }, - async expectDeleteIndexButtonExistsInMoreOptions() { - await testSubjects.existOrFail('moreOptionsDeleteIndex'); + async expectDeleteIndexButtonExists() { + await testSubjects.existOrFail('deleteIndexMenuButton'); }, async clickDeleteIndexButton() { - await testSubjects.click('moreOptionsDeleteIndex'); - }, - async expectDeleteIndexModalExists() { - await testSubjects.existOrFail('deleteIndexActionModal'); + await testSubjects.click('deleteIndexMenuButton'); }, async clickConfirmingDeleteIndex() { await testSubjects.existOrFail('confirmModalConfirmButton'); await testSubjects.click('confirmModalConfirmButton'); }, + async expectPageLoadErrorExists() { await retry.tryForTime(60 * 1000, async () => { - await testSubjects.existOrFail('pageLoadError'); + await testSubjects.existOrFail('indexDetailsErrorLoadingDetails'); }); - await testSubjects.existOrFail('loadingErrorBackToIndicesButton'); - await testSubjects.existOrFail('reloadButton'); - }, - async expectIndexNotFoundErrorExists() { - const pageLoadErrorElement = await ( - await testSubjects.find('pageLoadError') - ).findByClassName('euiTitle'); - expect(await pageLoadErrorElement.getVisibleText()).to.contain('Not Found'); + await testSubjects.existOrFail('indexDetailsReloadDetailsButton'); }, async hasPageReloadButton() { - await testSubjects.existOrFail('reloadButton'); + await testSubjects.existOrFail('indexDetailsReloadDetailsButton'); }, async pageReloadButtonIsVisible() { - return testSubjects.isDisplayed('reloadButton'); + return testSubjects.isDisplayed('indexDetailsReloadDetailsButton'); }, async clickPageReload() { await retry.tryForTime( 60 * 1000, async () => { - await testSubjects.click('reloadButton', 2000); + await testSubjects.click('indexDetailsReloadDetailsButton', 2000); }, undefined, 100 ); }, async expectTabsExists() { - await testSubjects.existOrFail('mappingsTab', { timeout: 2000 }); - await testSubjects.existOrFail('dataTab', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsTab-mappings', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsTab-overview', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsTab-settings', { timeout: 2000 }); }, - async changeTab(tab: 'dataTab' | 'mappingsTab' | 'settingsTab') { + async changeTab( + tab: 'indexDetailsTab-overview' | 'indexDetailsTab-mappings' | 'indexDetailsTab-settings' + ) { await testSubjects.click(tab); }, - async expectUrlShouldChangeTo(tab: 'data' | 'mappings' | 'settings') { - expect(await browser.getCurrentUrl()).contain(`/${tab}`); + async expectUrlShouldChangeTo(tab: 'overview' | 'mappings' | 'settings') { + expect(await browser.getCurrentUrl()).contain(`tab=${tab}`); }, async expectMappingsComponentIsVisible() { await testSubjects.existOrFail('indexDetailsMappingsToggleViewButton', { timeout: 2000 }); @@ -245,91 +160,15 @@ export function SearchIndexDetailPageProvider({ getService }: FtrProviderContext ); expect(isEditSettingsButtonDisabled).to.be(true); }, - async expectSelectedLanguage(language: string) { - await testSubjects.existOrFail('codeExampleLanguageSelect'); - expect( - (await testSubjects.getVisibleText('codeExampleLanguageSelect')).toLowerCase() - ).contain(language); - }, - async selectCodingLanguage(language: string) { - await testSubjects.existOrFail('codeExampleLanguageSelect'); - await testSubjects.click('codeExampleLanguageSelect'); - await testSubjects.existOrFail(`lang-option-${language}`); - await testSubjects.click(`lang-option-${language}`); - expect( - (await testSubjects.getVisibleText('codeExampleLanguageSelect')).toLowerCase() - ).contain(language); - }, - async codeSampleContainsValue(subject: string, value: string) { - const tstSubjId = `${subject}-code-block`; - await testSubjects.existOrFail(tstSubjId); - expect(await testSubjects.getVisibleText(tstSubjId)).contain(value); - }, - async openConsoleCodeExample() { - await testSubjects.existOrFail('tryInConsoleButton'); - await testSubjects.click('tryInConsoleButton'); - }, - - async expectAPIKeyToBeVisibleInCodeBlock(apiKey: string) { - await testSubjects.existOrFail('ingestDataCodeExample-code-block'); - expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( - apiKey - ); - }, - - async expectHasSampleDocuments() { - await testSubjects.existOrFail('ingestDataCodeExample-code-block'); - expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( - 'Yellowstone National Park' - ); - expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( - 'Yosemite National Park' - ); - expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( - 'Rocky Mountain National Park' - ); - }, - - async clickFirstDocumentDeleteAction() { - await testSubjects.existOrFail('documentMetadataButton'); - await testSubjects.click('documentMetadataButton'); - await testSubjects.existOrFail('deleteDocumentButton'); - await testSubjects.click('deleteDocumentButton'); - }, - async expectDeleteDocumentActionNotVisible() { - await testSubjects.existOrFail('documentMetadataButton'); - await testSubjects.click('documentMetadataButton'); - await testSubjects.missingOrFail('deleteDocumentButton'); - }, - async expectDeleteDocumentActionIsDisabled() { - await testSubjects.existOrFail('documentMetadataButton'); - await testSubjects.click('documentMetadataButton'); - await testSubjects.existOrFail('deleteDocumentButton'); - const isDeleteDocumentEnabled = await testSubjects.isEnabled('deleteDocumentButton'); - expect(isDeleteDocumentEnabled).to.be(false); - await testSubjects.moveMouseTo('deleteDocumentButton'); - await testSubjects.existOrFail('deleteDocumentButtonToolTip'); - }, - async expectDeleteDocumentActionToBeEnabled() { - await testSubjects.existOrFail('documentMetadataButton'); - await testSubjects.click('documentMetadataButton'); - await testSubjects.existOrFail('deleteDocumentButton'); - const isDeleteDocumentEnabled = await testSubjects.isEnabled('deleteDocumentButton'); - expect(isDeleteDocumentEnabled).to.be(true); - }, async openIndicesDetailFromIndexManagementIndicesListTable(indexOfRow: number) { const indexList = await testSubjects.findAll('indexTableIndexNameLink'); await indexList[indexOfRow].click(); await retry.waitFor('index details page title to show up', async () => { - return (await testSubjects.isDisplayed('searchIndexDetailsHeader')) === true; + return (await testSubjects.isDisplayed('indexDetailsHeader')) === true; }); }, - async expectIndexNametoBeInBreadcrumbs(indexName: string) { - await testSubjects.existOrFail('euiBreadcrumb'); - expect(await testSubjects.getVisibleText('breadcrumb last')).to.contain(indexName); - }, async expectBreadcrumbsToBeAvailable(breadcrumbsName: string) { const breadcrumbs = await testSubjects.findAll('euiBreadcrumb'); let isBreadcrumbShown: boolean = false; @@ -365,11 +204,20 @@ export function SearchIndexDetailPageProvider({ getService }: FtrProviderContext expect(isMappingsFieldEnabled).to.be(true); }, - async dismissIngestTourIfShown() { - if (await testSubjects.exists('searchIngestTourCloseButton')) { - await testSubjects.click('searchIngestTourCloseButton'); - await testSubjects.missingOrFail('searchIngestTourCloseButton', { timeout: 2000 }); - } + async expectDiscoverLinkExists() { + await testSubjects.existOrFail('discoverButtonLink', { timeout: 2000 }); + }, + + async expectAddDataSectionExists() { + await testSubjects.existOrFail('codeBlockControlsPanel', { timeout: 2000 }); + }, + async expectDataPreviewExists() { + await retry.try(async () => { + await testSubjects.existOrFail('search-index-documents-result', { timeout: 5000 }); + }); + }, + async expectDataPreviewNotExists() { + await testSubjects.missingOrFail('search-index-documents-result', { timeout: 2000 }); }, }; } diff --git a/x-pack/solutions/search/test/page_objects/search_playground_page.ts b/x-pack/solutions/search/test/page_objects/search_playground_page.ts index fe8b0704ca035..768969c86280f 100644 --- a/x-pack/solutions/search/test/page_objects/search_playground_page.ts +++ b/x-pack/solutions/search/test/page_objects/search_playground_page.ts @@ -202,29 +202,6 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) async expectCreateIndexButtonToExists() { await testSubjects.existOrFail('createIndexButton'); }, - async expectToBeOnCreateIndexPage() { - expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/indices/create'); - await testSubjects.existOrFail('elasticsearchCreateIndexPage', { timeout: 2000 }); - }, - async expectToBeOnIndexDetailsPage() { - await retry.tryForTime(60 * 1000, async () => { - expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/indices/index_details'); - }); - }, - async setIndexNameValue(value: string) { - await testSubjects.existOrFail('indexNameField'); - await testSubjects.setValue('indexNameField', value); - }, - async expectCreateIndexButtonToBeEnabled() { - await testSubjects.existOrFail('createIndexBtn'); - expect(await testSubjects.isEnabled('createIndexBtn')).equal(true); - }, - async clickCreateIndexButton() { - await testSubjects.existOrFail('createIndexBtn'); - expect(await testSubjects.isEnabled('createIndexBtn')).equal(true); - await testSubjects.click('createIndexBtn'); - }, - async expectOpenFlyoutAndSelectIndex() { await browser.refresh(); await selectIndex(); diff --git a/x-pack/solutions/search/test/serverless/api_integration/configs/config.feature_flags.ts b/x-pack/solutions/search/test/serverless/api_integration/configs/config.feature_flags.ts deleted file mode 100644 index 8e5e21dd6fc4b..0000000000000 --- a/x-pack/solutions/search/test/serverless/api_integration/configs/config.feature_flags.ts +++ /dev/null @@ -1,32 +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 { createTestConfig } from '@kbn/test-suites-xpack-platform/serverless/api_integration/config.base'; -import { services } from '../services'; - -/** - * Make sure to create a MKI deployment with custom Kibana image, that includes feature flags arguments - * These tests most likely will fail on default MKI project - */ -export default createTestConfig({ - serverlessProject: 'es', - services, - junit: { - reportName: 'Serverless Search Feature Flags API Integration Tests', - }, - suiteTags: { exclude: ['skipSvlSearch'] }, - // add feature flags - kbnServerArgs: [ - `--xpack.searchIndices.enabled=true`, // global empty state FF - ], - // load tests in the index file - testFiles: [require.resolve('./index.feature_flags.ts')], - - // include settings from project controller - // https://github.com/elastic/project-controller/blob/main/internal/project/esproject/config/elasticsearch.yml - esServerArgs: [], -}); diff --git a/x-pack/solutions/search/test/serverless/api_integration/configs/index.feature_flags.ts b/x-pack/solutions/search/test/serverless/api_integration/configs/index.feature_flags.ts deleted file mode 100644 index a78b08876e009..0000000000000 --- a/x-pack/solutions/search/test/serverless/api_integration/configs/index.feature_flags.ts +++ /dev/null @@ -1,14 +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 { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('Serverless search API - feature flags', function () { - loadTestFile(require.resolve('../test_suites/search_indices')); - }); -} diff --git a/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/indices.ts b/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/indices.ts deleted file mode 100644 index 9e38d428a22b9..0000000000000 --- a/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/indices.ts +++ /dev/null @@ -1,82 +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 expect from 'expect'; -import type { SupertestWithRoleScopeType } from '../../services'; -import type { FtrProviderContext } from '../../ftr_provider_context'; - -const INTERNAL_API_BASE_PATH = '/internal/search_indices'; - -export default function ({ getService }: FtrProviderContext) { - const log = getService('log'); - const esDeleteAllIndices = getService('esDeleteAllIndices'); - const roleScopedSupertest = getService('roleScopedSupertest'); - let supertestDeveloperWithCookieCredentials: SupertestWithRoleScopeType; - let supertestViewerWithCookieCredentials: SupertestWithRoleScopeType; - - describe('search_indices Indices APIs', function () { - describe('create index', function () { - const createIndexName = 'a-test-index'; - describe('developer', function () { - before(async () => { - supertestDeveloperWithCookieCredentials = - await roleScopedSupertest.getSupertestWithRoleScope('developer', { - useCookieHeader: true, - withInternalHeaders: true, - }); - }); - - after(async () => { - // Cleanup index created for testing purposes - try { - await esDeleteAllIndices(createIndexName); - } catch (err) { - log.debug('[Cleanup error] Error deleting index'); - throw err; - } - }); - - it('can create a new index', async () => { - const { body } = await supertestDeveloperWithCookieCredentials - .post(`${INTERNAL_API_BASE_PATH}/indices/create`) - .send({ - indexName: createIndexName, - }) - .expect(200); - - expect(body?.index).toBe(createIndexName); - }); - it('gives a conflict error if the index exists already', async () => { - await supertestDeveloperWithCookieCredentials - .post(`${INTERNAL_API_BASE_PATH}/indices/create`) - .send({ - indexName: createIndexName, - }) - .expect(409); - }); - }); - describe('viewer', function () { - before(async () => { - supertestViewerWithCookieCredentials = - await roleScopedSupertest.getSupertestWithRoleScope('viewer', { - useCookieHeader: true, - withInternalHeaders: true, - }); - }); - - it('cannot create a new index', async () => { - await supertestViewerWithCookieCredentials - .post(`${INTERNAL_API_BASE_PATH}/indices/create`) - .send({ - indexName: 'a-new-index', - }) - .expect(403); - }); - }); - }); - }); -} diff --git a/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/status.ts b/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/status.ts deleted file mode 100644 index 18fa539f01fdb..0000000000000 --- a/x-pack/solutions/search/test/serverless/api_integration/test_suites/search_indices/status.ts +++ /dev/null @@ -1,80 +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 expect from 'expect'; -import type { SupertestWithRoleScopeType } from '../../services'; -import type { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const roleScopedSupertest = getService('roleScopedSupertest'); - let supertestDeveloperWithCookieCredentials: SupertestWithRoleScopeType; - let supertestViewerWithCookieCredentials: SupertestWithRoleScopeType; - const testIndexName = 'search-test-index'; - - describe('search_indices Status APIs', function () { - describe('indices status', function () { - before(async () => { - supertestDeveloperWithCookieCredentials = - await roleScopedSupertest.getSupertestWithRoleScope('developer', { - useCookieHeader: true, - withInternalHeaders: true, - }); - }); - it('returns list of index names', async () => { - const { body } = await supertestDeveloperWithCookieCredentials - .get('/internal/search_indices/status') - .expect(200); - - expect(body.indexNames).toBeDefined(); - expect(Array.isArray(body.indexNames)).toBe(true); - }); - }); - describe('user privileges', function () { - // GET /internal/search_indices/start_privileges - describe('developer', function () { - it('returns expected privileges', async () => { - const { body } = await supertestDeveloperWithCookieCredentials - .get(`/internal/search_indices/start_privileges/${testIndexName}`) - .expect(200); - - expect(body).toEqual({ - privileges: { - canCreateApiKeys: true, - canDeleteDocuments: true, - canManageIndex: true, - }, - }); - }); - }); - }); - describe('viewer', function () { - before(async () => { - supertestViewerWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( - 'viewer', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - }); - - it('returns expected privileges', async () => { - const { body } = await supertestViewerWithCookieCredentials - .get(`/internal/search_indices/start_privileges/${testIndexName}`) - .expect(200); - - expect(body).toEqual({ - privileges: { - canCreateApiKeys: false, - canDeleteDocuments: false, - canManageIndex: false, - }, - }); - }); - }); - }); -} diff --git a/x-pack/solutions/search/test/serverless/functional/configs/config.ts b/x-pack/solutions/search/test/serverless/functional/configs/config.ts index 48c5f475c43da..a0aed2b71e122 100644 --- a/x-pack/solutions/search/test/serverless/functional/configs/config.ts +++ b/x-pack/solutions/search/test/serverless/functional/configs/config.ts @@ -44,9 +44,6 @@ export default createTestConfig({ searchPlayground: { pathname: '/app/search_playground', }, - elasticsearchIndices: { - pathname: '/app/elasticsearch/indices', - }, searchInferenceEndpoints: { pathname: '/app/management/ml/inference_endpoints', }, diff --git a/x-pack/solutions/search/test/serverless/functional/page_objects/index.ts b/x-pack/solutions/search/test/serverless/functional/page_objects/index.ts index 25336fe6428bd..4a45032d4add7 100644 --- a/x-pack/solutions/search/test/serverless/functional/page_objects/index.ts +++ b/x-pack/solutions/search/test/serverless/functional/page_objects/index.ts @@ -9,7 +9,6 @@ import { pageObjects as svlPlatformPageObjects } from '@kbn/test-suites-xpack-pl import { searchSharedPageObjects } from '../../../page_objects'; import { SvlSearchLandingPageProvider } from './svl_search_landing_page'; import { SvlSearchConnectorsPageProvider } from './svl_search_connectors_page'; -import { SvlSearchCreateIndexPageProvider } from './svl_search_create_index_page'; export const pageObjects = { ...svlPlatformPageObjects, @@ -18,5 +17,4 @@ export const pageObjects = { // Search Solution serverless FTR page objects svlSearchConnectorsPage: SvlSearchConnectorsPageProvider, svlSearchLandingPage: SvlSearchLandingPageProvider, - svlSearchCreateIndexPage: SvlSearchCreateIndexPageProvider, }; diff --git a/x-pack/solutions/search/test/serverless/functional/page_objects/svl_search_create_index_page.ts b/x-pack/solutions/search/test/serverless/functional/page_objects/svl_search_create_index_page.ts deleted file mode 100644 index 74f8641651e3e..0000000000000 --- a/x-pack/solutions/search/test/serverless/functional/page_objects/svl_search_create_index_page.ts +++ /dev/null @@ -1,106 +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 expect from '@kbn/expect'; -import type { FtrProviderContext } from '../ftr_provider_context'; - -export function SvlSearchCreateIndexPageProvider({ getService }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - const browser = getService('browser'); - const retry = getService('retry'); - - return { - async expectToBeOnCreateIndexPage() { - expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/indices/create'); - await testSubjects.existOrFail('elasticsearchCreateIndexPage', { timeout: 2000 }); - }, - async expectToBeOnIndexDetailsPage() { - await retry.tryForTime(60 * 1000, async () => { - expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/indices/index_details'); - }); - }, - async expectToBeOnIndexListPage() { - await retry.tryForTime(60 * 1000, async () => { - expect(await browser.getCurrentUrl()).contain( - '/app/elasticsearch/index_management/indices' - ); - }); - }, - async expectToBeOnMLFileUploadPage() { - await retry.tryForTime(60 * 1000, async () => { - expect(await browser.getCurrentUrl()).contain('/app/ml/filedatavisualizer'); - }); - }, - async expectIndexNameToExist() { - await testSubjects.existOrFail('indexNameField'); - }, - async setIndexNameValue(value: string) { - await testSubjects.existOrFail('indexNameField'); - await testSubjects.setValue('indexNameField', value); - }, - async expectCloseCreateIndexButtonExists() { - await testSubjects.existOrFail('closeCreateIndex'); - }, - async clickCloseCreateIndexButton() { - await testSubjects.existOrFail('closeCreateIndex'); - await testSubjects.click('closeCreateIndex'); - }, - async expectCreateIndexButtonToExist() { - await testSubjects.existOrFail('createIndexBtn'); - }, - async expectCreateIndexButtonToBeEnabled() { - await testSubjects.existOrFail('createIndexBtn'); - expect(await testSubjects.isEnabled('createIndexBtn')).equal(true); - }, - async expectCreateIndexButtonToBeDisabled() { - await testSubjects.existOrFail('createIndexBtn'); - expect(await testSubjects.isEnabled('createIndexBtn')).equal(false); - }, - async clickCreateIndexButton() { - await testSubjects.existOrFail('createIndexBtn'); - expect(await testSubjects.isEnabled('createIndexBtn')).equal(true); - await testSubjects.click('createIndexBtn'); - }, - async expectCreateIndexCodeView() { - await testSubjects.existOrFail('createIndexCodeView'); - }, - async expectCreateIndexUIView() { - await testSubjects.existOrFail('createIndexUIView'); - }, - async clickUIViewButton() { - await testSubjects.existOrFail('createIndexUIViewBtn'); - await testSubjects.click('createIndexUIViewBtn'); - }, - async clickCodeViewButton() { - await testSubjects.existOrFail('createIndexCodeViewBtn'); - await testSubjects.click('createIndexCodeViewBtn'); - }, - async clickFileUploadLink() { - await testSubjects.existOrFail('uploadFileLink'); - await testSubjects.click('uploadFileLink'); - }, - async expectAPIKeyVisibleInCodeBlock(apiKey: string) { - await testSubjects.existOrFail('createIndex-code-block'); - await retry.try(async () => { - expect(await testSubjects.getVisibleText('createIndex-code-block')).to.contain(apiKey); - }); - }, - - async expectAPIKeyPreGenerated() { - await testSubjects.existOrFail('apiKeyHasBeenGenerated'); - }, - - async expectAPIKeyNotPreGenerated() { - await testSubjects.existOrFail('apiKeyHasNotBeenGenerated'); - }, - - async expectAPIKeyFormNotAvailable() { - await testSubjects.missingOrFail('apiKeyHasNotBeenGenerated'); - await testSubjects.missingOrFail('apiKeyHasBeenGenerated'); - }, - }; -} diff --git a/x-pack/solutions/search/test/serverless/functional/services/svl_search_navigation.ts b/x-pack/solutions/search/test/serverless/functional/services/svl_search_navigation.ts index 5835dc67168d2..487eca8ed3b25 100644 --- a/x-pack/solutions/search/test/serverless/functional/services/svl_search_navigation.ts +++ b/x-pack/solutions/search/test/serverless/functional/services/svl_search_navigation.ts @@ -41,11 +41,13 @@ export function SvlSearchNavigationServiceProvider({ }, async navigateToIndexDetailPage(indexName: string) { await retry.tryForTime(60 * 1000, async () => { - await PageObjects.common.navigateToApp(`elasticsearch/indices/index_details/${indexName}`, { + await PageObjects.common.navigateToApp('indexManagement', { + path: 'indices/index_details', + search: `indexName=${indexName}`, shouldLoginIfPrompted: false, }); }); - await testSubjects.existOrFail('searchIndicesDetailsPage', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsContent', { timeout: 2000 }); }, async navigateToInferenceManagementPage() { await PageObjects.common.navigateToApp('searchInferenceEndpoints', { diff --git a/x-pack/solutions/search/test/serverless/functional/test_suites/index_management.ts b/x-pack/solutions/search/test/serverless/functional/test_suites/index_management.ts index 7114c21438e09..dc448c7ccb3bf 100644 --- a/x-pack/solutions/search/test/serverless/functional/test_suites/index_management.ts +++ b/x-pack/solutions/search/test/serverless/functional/test_suites/index_management.ts @@ -17,15 +17,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'common', 'header', 'indexManagement', - 'svlSearchCreateIndexPage', ]); const browser = getService('browser'); const security = getService('security'); - const es = getService('es'); + const toasts = getService('toasts'); const esDeleteAllIndices = getService('esDeleteAllIndices'); const testIndexName = `test-index-ftr-${Math.random()}`; - const testAPIIndexName = `test-api-index-ftr-${Math.random()}`; describe('index management', function () { // see details: https://github.com/elastic/kibana/issues/200878 this.tags(['failsOnMKI']); @@ -39,7 +37,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.header.waitUntilLoadingHasFinished(); }); after(async () => { - await esDeleteAllIndices([testIndexName, testAPIIndexName]); + await esDeleteAllIndices([testIndexName, 'search-*']); }); it('renders the indices tab', async () => { @@ -58,46 +56,29 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.indexManagement.changeTabs('indicesTab'); await pageObjects.header.waitUntilLoadingHasFinished(); }); - it('can create an index', async () => { + it('can create an index with the prepopulated name', async () => { await pageObjects.indexManagement.clickCreateIndexButton(); - await pageObjects.svlSearchCreateIndexPage.expectToBeOnCreateIndexPage(); - await pageObjects.svlSearchCreateIndexPage.expectCreateIndexUIView(); - await pageObjects.svlSearchCreateIndexPage.expectCreateIndexButtonToBeEnabled(); - await pageObjects.svlSearchCreateIndexPage.setIndexNameValue(testIndexName); - await pageObjects.svlSearchCreateIndexPage.clickCreateIndexButton(); - await pageObjects.svlSearchCreateIndexPage.expectToBeOnIndexDetailsPage(); - await pageObjects.common.navigateToApp('indexManagement'); - await pageObjects.indexManagement.changeTabs('indicesTab'); - await pageObjects.indexManagement.expectIndexToExist(testIndexName); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); + const title = await toasts.getTitleAndDismiss(); + expect(title).to.contain('Successfully created index'); }); - it('should redirect to index details when index is created via API and on the code view', async () => { + it('can create an index with a custom name', async () => { await pageObjects.indexManagement.clickCreateIndexButton(); - - await pageObjects.svlSearchCreateIndexPage.expectToBeOnCreateIndexPage(); - await pageObjects.svlSearchCreateIndexPage.expectCreateIndexUIView(); - await pageObjects.svlSearchCreateIndexPage.clickCodeViewButton(); - await pageObjects.svlSearchCreateIndexPage.expectCreateIndexCodeView(); - await es.indices.create({ index: testAPIIndexName }); - await pageObjects.svlSearchCreateIndexPage.expectToBeOnIndexDetailsPage(); + await pageObjects.indexManagement.setCreateIndexName(testIndexName); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); + await pageObjects.indexManagement.expectIndexToExist(testIndexName); }); - it('should have file upload link', async () => { + it('should show API code when "Show API" button is clicked', async () => { await pageObjects.indexManagement.clickCreateIndexButton(); - await pageObjects.svlSearchCreateIndexPage.expectToBeOnCreateIndexPage(); - await pageObjects.svlSearchCreateIndexPage.clickFileUploadLink(); - await pageObjects.svlSearchCreateIndexPage.expectToBeOnMLFileUploadPage(); - }); - it('should support closing create index page', async () => { - await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.indexManagement.clickCreateIndexShowApiButton(); - await pageObjects.svlSearchCreateIndexPage.expectCloseCreateIndexButtonExists(); - await pageObjects.svlSearchCreateIndexPage.clickCloseCreateIndexButton(); - await pageObjects.svlSearchCreateIndexPage.expectToBeOnIndexListPage(); - }); - it('should have the embedded console', async () => { - await pageObjects.indexManagement.clickCreateIndexButton(); + const buttonText = await pageObjects.indexManagement.getCreateIndexShowApiButtonText(); + expect(buttonText).to.be('Hide API'); - await testHasEmbeddedConsole(pageObjects); + const codeContent = await pageObjects.indexManagement.getCreateIndexApiCodeBlockContent(); + expect(codeContent).to.contain('PUT'); + expect(codeContent).to.contain('"mode":"standard"'); }); }); diff --git a/x-pack/solutions/search/test/serverless/functional/test_suites/search_index_detail.ts b/x-pack/solutions/search/test/serverless/functional/test_suites/search_index_detail.ts index 0a8cc315279f2..f5c2ef2558b26 100644 --- a/x-pack/solutions/search/test/serverless/functional/test_suites/search_index_detail.ts +++ b/x-pack/solutions/search/test/serverless/functional/test_suites/search_index_detail.ts @@ -25,8 +25,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ]); const svlSearchNavigation = getService('svlSearchNavigation'); const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - const retry = getService('retry'); const esDeleteAllIndices = getService('esDeleteAllIndices'); @@ -71,165 +69,105 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('can load index detail page', async () => { await pageObjects.searchIndexDetailsPage.expectIndexDetailPageHeader(); await pageObjects.searchIndexDetailsPage.expectSearchIndexDetailsTabsExists(); - await pageObjects.searchIndexDetailsPage.expectAPIReferenceDocLinkExists(); - await pageObjects.searchIndexDetailsPage.expectAPIReferenceDocLinkMissingInMoreOptions(); }); it('should have embedded dev console', async () => { await testHasEmbeddedConsole(pageObjects); }); it('should have breadcrumb navigation', async () => { - await pageObjects.searchIndexDetailsPage.expectIndexNametoBeInBreadcrumbs( - indexWithoutDataName - ); - await pageObjects.searchIndexDetailsPage.clickOnBreadcrumb('Index Management'); + await pageObjects.searchIndexDetailsPage.clickOnBreadcrumb('Indices'); await pageObjects.indexManagement.expectToBeOnIndexManagement(); await svlSearchNavigation.navigateToIndexDetailPage(indexWithoutDataName); }); - it('should have connection details', async () => { + it('should have connection details button', async () => { await pageObjects.searchIndexDetailsPage.expectConnectionDetails(); }); - - it('should have basic example texts', async () => { - await pageObjects.searchIndexDetailsPage.expectHasSampleDocuments(); + it('should open connection details flyout', async () => { + await pageObjects.searchIndexDetailsPage.expectConnectionDetailsFlyoutToOpen(); + await pageObjects.searchIndexDetailsPage.closeConnectionDetailsFlyout(); }); it('should have quick stats', async () => { - await pageObjects.searchIndexDetailsPage.expectStatelessQuickStats(); - await pageObjects.searchIndexDetailsPage.expectQuickStatsAIMappings(); + await pageObjects.searchIndexDetailsPage.expectSizeDocCountQuickStats(); }); - it('should show code examples for adding documents', async () => { - await pageObjects.searchIndexDetailsPage.expectAddDocumentCodeExamples(); - await pageObjects.searchIndexDetailsPage.expectSelectedLanguage('python'); - await pageObjects.searchIndexDetailsPage.codeSampleContainsValue( - 'installCodeExample', - 'pip install' - ); - await pageObjects.searchIndexDetailsPage.selectCodingLanguage('javascript'); - await pageObjects.searchIndexDetailsPage.codeSampleContainsValue( - 'installCodeExample', - 'npm install' - ); - await pageObjects.searchIndexDetailsPage.selectCodingLanguage('curl'); - await pageObjects.searchIndexDetailsPage.openConsoleCodeExample(); - await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen(); - await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar(); + it('should have add data section', async () => { + await pageObjects.searchIndexDetailsPage.expectAddDataSectionExists(); + }); + it('should not show data preview for empty index', async () => { + await pageObjects.searchIndexDetailsPage.expectDataPreviewNotExists(); }); describe('With data', () => { before(async () => { await svlSearchNavigation.navigateToIndexDetailPage(indexWithDataName); }); - it('should have index documents', async () => { - await pageObjects.searchIndexDetailsPage.expectHasIndexDocuments(); - }); - it('menu action item should be replaced with playground', async () => { - await pageObjects.searchIndexDetailsPage.expectActionItemReplacedWhenHasDocs(); - }); - it('should have link to API reference doc link in options menu', async () => { - await pageObjects.searchIndexDetailsPage.clickMoreOptionsActionsButton(); - await pageObjects.searchIndexDetailsPage.expectAPIReferenceDocLinkExistsInMoreOptions(); - }); it('should have documents in quick stats', async () => { - await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveDocumentCount(46); + await pageObjects.searchIndexDetailsPage.expectSizeDocCountToHaveDocumentCount(46); + }); + it('should show data preview', async () => { + await pageObjects.searchIndexDetailsPage.expectDataPreviewExists(); }); - it('should have with data tabs', async () => { + it('should have overview, mappings, and settings tabs', async () => { await pageObjects.searchIndexDetailsPage.expectTabsExists(); - await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('data'); }); it('should be able to change tabs to mappings and mappings is shown', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('mappingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-mappings'); await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('mappings'); await pageObjects.searchIndexDetailsPage.expectMappingsComponentIsVisible(); }); it('should be able to change tabs to settings and settings is shown', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('settingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-settings'); await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('settings'); await pageObjects.searchIndexDetailsPage.expectSettingsComponentIsVisible(); }); - it('should be able to delete document', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('dataTab'); - await pageObjects.searchIndexDetailsPage.clickFirstDocumentDeleteAction(); - - // re-open page to refresh queries for test (these will auto-refresh, - // but waiting for that will make this test flakey) - await browser.refresh(); - await retry.tryWithRetries( - 'Wait for document count to update', - async () => { - await pageObjects.searchIndexDetailsPage.expectQuickStatsToHaveDocumentCount(45); - }, - { - retryCount: 5, - retryDelay: 1000, - }, - async () => { - await browser.refresh(); - } - ); - }); - }); - describe('With dense vectors', () => { - it('should have ai quick stats for index with semantic mappings', async () => { - await svlSearchNavigation.navigateToIndexDetailPage(indexWithDenseVectorName); - await pageObjects.searchIndexDetailsPage.expectQuickStatsAIMappingsToHaveVectorFields(); - }); }); describe('has index actions enabled', () => { beforeEach(async () => { - await svlSearchNavigation.navigateToIndexDetailPage(indexWithDenseVectorName); + await svlSearchNavigation.navigateToIndexDetailPage(indexWithDataName); }); - it('delete document button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.expectDeleteDocumentActionToBeEnabled(); - }); it('add field button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('mappingsTab'); - await pageObjects.searchIndexDetailsPage.dismissIngestTourIfShown(); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-mappings'); await pageObjects.searchIndexDetailsPage.expectAddFieldToBeEnabled(); }); it('edit settings button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('settingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-settings'); await pageObjects.searchIndexDetailsPage.expectEditSettingsToBeEnabled(); }); - it('delete index button is enabled', async () => { - await pageObjects.searchIndexDetailsPage.expectMoreOptionsActionButtonExists(); - await pageObjects.searchIndexDetailsPage.clickMoreOptionsActionsButton(); - await pageObjects.searchIndexDetailsPage.expectMoreOptionsOverviewMenuIsShown(); - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExistsInMoreOptions(); - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonToBeEnabled(); + it('delete index button exists', async () => { + await pageObjects.searchIndexDetailsPage.expectManageIndexButtonExists(); + await pageObjects.searchIndexDetailsPage.clickManageIndexButton(); + await pageObjects.searchIndexDetailsPage.expectManageIndexContextMenuIsShown(); + await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExists(); }); }); describe('page loading error', () => { before(async () => { - // manually navigate to index detail page for an index that doesn't exist - await pageObjects.common.navigateToApp( - `elasticsearch/indices/index_details/${indexDoesNotExistName}`, - { - shouldLoginIfPrompted: false, - } - ); + await pageObjects.common.navigateToApp('indexManagement', { + path: 'indices/index_details', + search: `indexName=${indexDoesNotExistName}`, + shouldLoginIfPrompted: false, + }); }); it('has page load error section', async () => { await pageObjects.searchIndexDetailsPage.expectPageLoadErrorExists(); - await pageObjects.searchIndexDetailsPage.expectIndexNotFoundErrorExists(); }); }); - describe('Index more options menu', () => { + describe('Index manage menu', () => { before(async () => { await svlSearchNavigation.navigateToIndexDetailPage(indexWithDenseVectorName); }); - it('shows action menu in actions popover', async () => { - await pageObjects.searchIndexDetailsPage.expectMoreOptionsActionButtonExists(); - await pageObjects.searchIndexDetailsPage.clickMoreOptionsActionsButton(); - await pageObjects.searchIndexDetailsPage.expectMoreOptionsOverviewMenuIsShown(); + it('shows action menu in manage index popover', async () => { + await pageObjects.searchIndexDetailsPage.expectManageIndexButtonExists(); + await pageObjects.searchIndexDetailsPage.clickManageIndexButton(); + await pageObjects.searchIndexDetailsPage.expectManageIndexContextMenuIsShown(); }); it('should delete index', async () => { - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExistsInMoreOptions(); + await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExists(); await pageObjects.searchIndexDetailsPage.clickDeleteIndexButton(); await pageObjects.searchIndexDetailsPage.clickConfirmingDeleteIndex(); }); @@ -250,7 +188,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('navigates to overview tab', async () => { await pageObjects.indexManagement.changeManageIndexTab('showOverviewIndexMenuButton'); await pageObjects.searchIndexDetailsPage.expectIndexDetailPageHeader(); - await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('data'); + await pageObjects.searchIndexDetailsPage.expectUrlShouldChangeTo('overview'); }); it('navigates to settings tab', async () => { @@ -267,7 +205,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('viewer', function () { + // TODO: Fix this test to accurately test the viewer role once it's decided what the viewer role should be able to see. + describe.skip('viewer', function () { describe('search index details page', function () { before(async () => { await pageObjects.svlCommonPage.loginAsViewer(); @@ -275,24 +214,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { beforeEach(async () => { await svlSearchNavigation.navigateToIndexDetailPage(indexWithDataName); }); - it('delete document button is disabled', async () => { - await pageObjects.searchIndexDetailsPage.expectDeleteDocumentActionIsDisabled(); - }); it('add field button is disabled', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('mappingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-mappings'); await pageObjects.searchIndexDetailsPage.expectAddFieldToBeDisabled(); }); it('edit settings button is disabled', async () => { - await pageObjects.searchIndexDetailsPage.changeTab('settingsTab'); + await pageObjects.searchIndexDetailsPage.changeTab('indexDetailsTab-settings'); await pageObjects.searchIndexDetailsPage.expectEditSettingsIsDisabled(); }); - it('delete index button is disabled', async () => { - await pageObjects.searchIndexDetailsPage.expectMoreOptionsActionButtonExists(); - await pageObjects.searchIndexDetailsPage.clickMoreOptionsActionsButton(); - await pageObjects.searchIndexDetailsPage.expectMoreOptionsOverviewMenuIsShown(); - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonExistsInMoreOptions(); - await pageObjects.searchIndexDetailsPage.expectDeleteIndexButtonToBeDisabled(); - }); it('show no privileges to create api key', async () => { await pageObjects.svlApiKeys.expectAPIKeyNoPrivileges(); }); diff --git a/x-pack/solutions/search/test/serverless/functional/test_suites/search_playground/playground_overview.ts b/x-pack/solutions/search/test/serverless/functional/test_suites/search_playground/playground_overview.ts index 6561f0c26a0bb..f6812f1faa408 100644 --- a/x-pack/solutions/search/test/serverless/functional/test_suites/search_playground/playground_overview.ts +++ b/x-pack/solutions/search/test/serverless/functional/test_suites/search_playground/playground_overview.ts @@ -23,7 +23,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'searchPlayground', 'embeddedConsole', 'solutionNavigation', - 'svlSearchCreateIndexPage', + 'indexManagement', ]); const svlSearchNavigation = getService('svlSearchNavigation'); const svlCommonApi = getService('svlCommonApi'); @@ -190,11 +190,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('should be able to create index from UI', async () => { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToExists(); await pageObjects.searchPlayground.PlaygroundStartChatPage.clickCreateIndex(); - await pageObjects.svlSearchCreateIndexPage.expectToBeOnCreateIndexPage(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.setIndexNameValue(indexName); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToBeEnabled(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.clickCreateIndexButton(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToBeOnIndexDetailsPage(); + await pageObjects.indexManagement.expectToBeOnIndexManagement(); + await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.indexManagement.setCreateIndexName(indexName); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); // add mapping await es.indices.putMapping({