Skip to content

Commit

Permalink
[Onboarding] Introduce search index details page locator and update a…
Browse files Browse the repository at this point in the history
…ll reference (elastic#205005)

## Summary

* Introducing new locator for onboarding `search_indices` plugin index
details page - `SEARCH_INDEX_DETAILS_LOCATOR_ID`.
* In stack, Updated view index details usage(connector table, connector
details page, search application & web crawler) to use this locator to
navigate to onboarding index details page ONLY when its `search -
Elasticsearch solution nav`
* Index management view index details would use extensionService with
active solution id check in search_indices plugin
* verified locally existing FTR & unit tests 
* Added FTR for index management in functional_search tests for search
solution nav and classic nav


https://github.com/user-attachments/assets/8f0fea00-3dce-449e-805a-b3cf317f4066



### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 10, 2025
1 parent b6176b2 commit 54436e3
Show file tree
Hide file tree
Showing 21 changed files with 1,170 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';

import { useActions, useValues } from 'kea';

Expand All @@ -22,26 +22,27 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';
import { EnterpriseSearchApplicationIndex } from '../../../../../common/types/search_applications';

import { SEARCH_INDEX_PATH } from '../../../enterprise_search_content/routes';
import { CANCEL_BUTTON_LABEL } from '../../../shared/constants';
import { indexHealthToHealthColor } from '../../../shared/constants/health_colors';
import { generateEncodedPath } from '../../../shared/encode_path_params';
import { KibanaLogic } from '../../../shared/kibana';
import { EuiLinkTo } from '../../../shared/react_router_helpers';
import { TelemetryLogic } from '../../../shared/telemetry/telemetry_logic';

import { SearchApplicationIndicesLogic } from './search_application_indices_logic';
import { SearchApplicationViewIndexLink } from './search_application_view_index_link';

export const SearchApplicationIndices: React.FC = () => {
const subduedBackground = useEuiBackgroundColor('subdued');
const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic);
const { searchApplicationData } = useValues(SearchApplicationIndicesLogic);
const { removeIndexFromSearchApplication } = useActions(SearchApplicationIndicesLogic);
const { navigateToUrl } = useValues(KibanaLogic);
const { navigateToUrl, share } = useValues(KibanaLogic);
const [removeIndexConfirm, setConfirmRemoveIndex] = useState<string | null>(null);
const searchIndicesLocator = useMemo(
() => share?.url.locators.get('SEARCH_INDEX_DETAILS_LOCATOR_ID'),
[share]
);

if (!searchApplicationData) return null;
const { indices } = searchApplicationData;
Expand Down Expand Up @@ -94,15 +95,10 @@ export const SearchApplicationIndices: React.FC = () => {
health === 'unknown' ? (
name
) : (
<EuiLinkTo
data-test-subj="search-application-index-link"
to={`${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${generateEncodedPath(SEARCH_INDEX_PATH, {
indexName: name,
})}`}
shouldNotCreateHref
>
{name}
</EuiLinkTo>
<SearchApplicationViewIndexLink
indexName={name}
dataTestSubj="search-application-index-link"
/>
),
sortable: ({ name }: EnterpriseSearchApplicationIndex) => name,
truncateText: true,
Expand Down Expand Up @@ -148,6 +144,7 @@ export const SearchApplicationIndices: React.FC = () => {
{
actions: [
{
enabled: () => searchIndicesLocator !== undefined,
available: (index) => index.health !== 'unknown',
'data-test-subj': 'search-application-view-index-btn',
description: i18n.translate(
Expand All @@ -168,15 +165,19 @@ export const SearchApplicationIndices: React.FC = () => {
},
}
),
onClick: (index) =>
navigateToUrl(
`${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/${generateEncodedPath(SEARCH_INDEX_PATH, {
indexName: index.name,
})}`,
{

onClick: async (index) => {
if (searchIndicesLocator) {
const url = await searchIndicesLocator.getUrl({ indexName: index.name });
navigateToUrl(url, {
shouldNotCreateHref: true,
}
),
shouldNotPrepend: true,
});
} else {
return undefined;
}
},

type: 'icon',
},
...(indices.length > 1 ? [removeIndexAction] : []),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';

import { useValues } from 'kea';

import { EuiLink } from '@elastic/eui';

import { KibanaLogic } from '../../../shared/kibana';

export const SearchApplicationViewIndexLink: React.FC<{
indexName: string;
dataTestSubj?: string;
dataTelemetryId?: string;
}> = ({ indexName, dataTestSubj, dataTelemetryId }) => {
const { share } = useValues(KibanaLogic);

const searchIndexDetailsUrl: string =
share?.url.locators.get('SEARCH_INDEX_DETAILS_LOCATOR_ID')?.useUrl({ indexName }) ?? '';

return searchIndexDetailsUrl ? (
<EuiLink
data-telemetry-id={dataTelemetryId}
data-test-subj={dataTestSubj}
href={searchIndexDetailsUrl}
>
{indexName}
</EuiLink>
) : (
<>{indexName}</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ import { i18n } from '@kbn/i18n';

import { FormattedMessage } from '@kbn/i18n-react';

import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';

import { EnterpriseSearchApplicationIndex } from '../../../../../common/types/search_applications';

import { SEARCH_INDEX_PATH } from '../../../enterprise_search_content/routes';
import { healthColorsMap } from '../../../shared/constants/health_colors';
import { generateEncodedPath } from '../../../shared/encode_path_params';
import { EuiLinkTo } from '../../../shared/react_router_helpers';

import { SearchApplicationViewIndexLink } from '../search_application/search_application_view_index_link';

import { SearchApplicationIndicesFlyoutLogic } from './search_application_indices_flyout_logic';

Expand All @@ -58,16 +55,11 @@ export const SearchApplicationIndicesFlyout: React.FC = () => {
}
),
render: (indexName: string) => (
<EuiLinkTo
data-test-subj="search-application-index-link"
data-telemetry-id="entSearchApplications-list-viewIndex"
to={`${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${generateEncodedPath(SEARCH_INDEX_PATH, {
indexName,
})}`}
shouldNotCreateHref
>
{indexName}
</EuiLinkTo>
<SearchApplicationViewIndexLink
indexName={indexName}
dataTestSubj="search-application-index-link"
dataTelemetryId="entSearchApplications-list-viewIndex"
/>
),
sortable: true,
truncateText: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import { generateEncodedPath } from '../../../../shared/encode_path_params';
import { EuiLinkTo } from '../../../../shared/react_router_helpers';

import { ApiKey } from '../../../api/connector/generate_connector_api_key_api_logic';
import { CONNECTOR_DETAIL_PATH, SEARCH_INDEX_PATH } from '../../../routes';
import { CONNECTOR_DETAIL_PATH } from '../../../routes';
import { ConnectorViewIndexLink } from '../../shared/connector_view_search_indices_details/connector_view_search_indices_details';

export interface GeneratedConfigFieldsProps {
apiKey?: ApiKey;
Expand Down Expand Up @@ -84,7 +85,6 @@ export const GeneratedConfigFields: React.FC<GeneratedConfigFieldsProps> = ({
isGenerateLoading,
}) => {
const [isModalVisible, setIsModalVisible] = useState(false);

const refreshButtonClick = () => {
setIsModalVisible(true);
};
Expand Down Expand Up @@ -184,15 +184,7 @@ export const GeneratedConfigFields: React.FC<GeneratedConfigFieldsProps> = ({
</EuiFlexItem>
<EuiFlexItem>
{connector.index_name && (
<EuiLinkTo
external
target="_blank"
to={generateEncodedPath(SEARCH_INDEX_PATH, {
indexName: connector.index_name,
})}
>
{connector.index_name}
</EuiLinkTo>
<ConnectorViewIndexLink indexName={connector.index_name} target />
)}
</EuiFlexItem>
<EuiFlexItem />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React from 'react';
import React, { useMemo } from 'react';

import { useValues } from 'kea';

Expand All @@ -32,6 +32,8 @@ import {
connectorStatusToText,
} from '../../utils/connector_status_helpers';

import { ConnectorViewIndexLink } from '../shared/connector_view_search_indices_details/connector_view_search_indices_details';

import { ConnectorType } from './connector_type';
import { ConnectorViewItem } from './connectors_logic';

Expand All @@ -57,7 +59,12 @@ export const ConnectorsTable: React.FC<ConnectorsTableProps> = ({
isLoading,
onDelete,
}) => {
const { navigateToUrl } = useValues(KibanaLogic);
const { navigateToUrl, share } = useValues(KibanaLogic);
const searchIndicesLocator = useMemo(
() => share?.url.locators.get('SEARCH_INDEX_DETAILS_LOCATOR_ID'),
[share]
);

const columns: Array<EuiBasicTableColumn<ConnectorViewItem>> = [
...(!isCrawler
? [
Expand Down Expand Up @@ -88,12 +95,8 @@ export const ConnectorsTable: React.FC<ConnectorsTableProps> = ({
),
render: (connector: ConnectorViewItem) =>
connector.index_name ? (
connector.indexExists ? (
<EuiLinkTo
to={generateEncodedPath(SEARCH_INDEX_PATH, { indexName: connector.index_name })}
>
{connector.index_name}
</EuiLinkTo>
connector.indexExists && searchIndicesLocator ? (
<ConnectorViewIndexLink indexName={connector.index_name} />
) : (
connector.index_name
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';

import { useValues } from 'kea';

import { EuiLink } from '@elastic/eui';

import { KibanaLogic } from '../../../../shared/kibana';

export const ConnectorViewIndexLink: React.FC<{
indexName: string;
target?: boolean;
}> = ({ indexName, target }) => {
const { share } = useValues(KibanaLogic);

const searchIndexDetailsUrl = share?.url.locators
.get('SEARCH_INDEX_DETAILS_LOCATOR_ID')
?.useUrl({ indexName });

return searchIndexDetailsUrl ? (
<EuiLink
target={target ? '_blank' : undefined}
external={target ?? false}
href={searchIndexDetailsUrl}
>
{indexName}
</EuiLink>
) : (
<>{indexName}</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export const GET_USER_PRIVILEGES_ROUTE = '/internal/search_indices/start_privile
export const POST_CREATE_INDEX_ROUTE = '/internal/search_indices/indices/create';

export const INDEX_DOCUMENT_ROUTE = '/internal/search_indices/{indexName}/documents/{id}';
export const SEARCH_DOCUMENTS_ROUTE = '/internal/search_indices/{indexName}/documents/search';
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const useIndexDocumentSearch = (indexName: string) => {
refetchIntervalInBackground: true,
refetchOnWindowFocus: 'always',
queryFn: async ({ signal }) =>
http.post<IndexDocuments>(`/internal/serverless_search/indices/${indexName}/search`, {
http.post<IndexDocuments>(`/internal/search_indices/${indexName}/documents/search`, {
body: JSON.stringify({
searchQuery: '',
trackTotalHits: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,12 @@
*/

import type { LocatorDefinition } from '@kbn/share-plugin/common';
import type { SharePluginSetup } from '@kbn/share-plugin/public';
import type { SerializableRecord } from '@kbn/utility-types';

import { INDICES_APP_ID } from '../common';
import { CREATE_INDEX_PATH } from './routes';
import { INDICES_APP_ID } from '../../common';
import { CREATE_INDEX_PATH } from '../routes';

export function registerLocators(share: SharePluginSetup) {
share.url.locators.create<SerializableRecord>(new CreateIndexLocatorDefinition());
}

class CreateIndexLocatorDefinition implements LocatorDefinition<SerializableRecord> {
export class CreateIndexLocatorDefinition implements LocatorDefinition<SerializableRecord> {
public readonly getLocation = async () => {
return {
app: INDICES_APP_ID,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { SharePluginSetup } from '@kbn/share-plugin/public';
import { SerializableRecord } from '@kbn/utility-types';
import { CreateIndexLocatorDefinition } from './create_index_locator';
import { SearchIndicesLocatorDefinition } from './search_indices_locator';

export function registerLocators(share: SharePluginSetup) {
share.url.locators.create<SerializableRecord>(new CreateIndexLocatorDefinition());
share.url.locators.create<SerializableRecord>(new SearchIndicesLocatorDefinition());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { LocatorDefinition } from '@kbn/share-plugin/common';
import type { SerializableRecord } from '@kbn/utility-types';

import { INDICES_APP_ID } from '../../common';
import { SearchIndexDetailsTabValues } from '../routes';

export interface SearchIndicesLocatorParams extends SerializableRecord {
indexName: string;
detailsTabId: string;
}

export class SearchIndicesLocatorDefinition
implements LocatorDefinition<SearchIndicesLocatorParams>
{
public readonly getLocation = async (params: SearchIndicesLocatorParams) => {
const path = `/index_details/${params.indexName}`;

return {
app: INDICES_APP_ID,
path: SearchIndexDetailsTabValues.includes(params.detailsTabId)
? `${path}/${params.detailsTabId}`
: path,
state: {},
};
};

public readonly id = 'SEARCH_INDEX_DETAILS_LOCATOR_ID';
}
Loading

0 comments on commit 54436e3

Please sign in to comment.