[Enterprise Search][Search Application] Rename API page to Connect (#155278)

## Summary

Renamed the engine API page to Connect and introduced a tab for the API
so we can add a Documentation tab next.

### Screenshots
<img width="1391" alt="image"
src="https://user-images.githubusercontent.com/1972968/233129001-0a6f3c8d-9df0-4a50-bb4e-c758a945bb2e.png">
This commit is contained in:
Rodney Norris 2023-04-20 08:22:32 -05:00 committed by GitHub
parent 517b0a0fb3
commit d8fa5d6831
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 184 additions and 87 deletions

View file

@ -0,0 +1,102 @@
/*
* 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 { useParams } from 'react-router-dom';
import { useValues } from 'kea';
import { i18n } from '@kbn/i18n';
import { generateEncodedPath } from '../../../../shared/encode_path_params';
import { KibanaLogic } from '../../../../shared/kibana';
import {
SEARCH_APPLICATION_CONNECT_PATH,
EngineViewTabs,
SearchApplicationConnectTabs,
} from '../../../routes';
import { EnterpriseSearchEnginesPageTemplate } from '../../layout/engines_page_template';
import { EngineError } from '../engine_error';
import { EngineViewLogic } from '../engine_view_logic';
import { SearchApplicationAPI } from './search_application_api';
const pageTitle = i18n.translate(
'xpack.enterpriseSearch.content.searchApplications.connect.pageTitle',
{
defaultMessage: 'Connect',
}
);
const API_TAB_TITLE = i18n.translate(
'xpack.enterpriseSearch.content.searchApplications.connect.apiTabTitle',
{
defaultMessage: 'API',
}
);
const ConnectTabs: string[] = Object.values(SearchApplicationConnectTabs);
const getTabBreadCrumb = (tabId: string) => {
switch (tabId) {
case SearchApplicationConnectTabs.API:
return API_TAB_TITLE;
default:
return tabId;
}
};
export const EngineConnect: React.FC = () => {
const { engineName, isLoadingEngine } = useValues(EngineViewLogic);
const { connectTabId = SearchApplicationConnectTabs.API } = useParams<{
connectTabId?: string;
}>();
const onTabClick = (tab: SearchApplicationConnectTabs) => () => {
KibanaLogic.values.navigateToUrl(
generateEncodedPath(SEARCH_APPLICATION_CONNECT_PATH, {
engineName,
connectTabId: tab,
})
);
};
if (!ConnectTabs.includes(connectTabId)) {
return (
<EnterpriseSearchEnginesPageTemplate
pageChrome={[engineName, pageTitle]}
pageViewTelemetry={EngineViewTabs.CONNECT}
isLoading={isLoadingEngine}
pageHeader={{
pageTitle,
rightSideItems: [],
}}
engineName={engineName}
>
<EngineError notFound />
</EnterpriseSearchEnginesPageTemplate>
);
}
return (
<EnterpriseSearchEnginesPageTemplate
pageChrome={[engineName, pageTitle, getTabBreadCrumb(connectTabId)]}
pageViewTelemetry={EngineViewTabs.CONNECT}
isLoading={isLoadingEngine}
pageHeader={{
pageTitle,
rightSideItems: [],
tabs: [
{
isSelected: connectTabId === SearchApplicationConnectTabs.API,
label: API_TAB_TITLE,
onClick: onTabClick(SearchApplicationConnectTabs.API),
},
],
}}
engineName={engineName}
>
{connectTabId === SearchApplicationConnectTabs.API && <SearchApplicationAPI />}
</EnterpriseSearchEnginesPageTemplate>
);
};

View file

@ -19,7 +19,6 @@ import {
EuiSteps,
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ANALYTICS_PLUGIN } from '../../../../../../common/constants';
@ -29,26 +28,19 @@ import { generateEncodedPath } from '../../../../shared/encode_path_params';
import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url';
import { KibanaLogic } from '../../../../shared/kibana';
import { EngineViewTabs } from '../../../routes';
import { EnterpriseSearchEnginesPageTemplate } from '../../layout/engines_page_template';
import { EngineIndicesLogic } from '../engine_indices_logic';
import { EngineViewLogic } from '../engine_view_logic';
import { EngineApiIntegrationStage } from './engine_api_integration';
import { EngineApiLogic } from './engine_api_logic';
import { GenerateEngineApiKeyModal } from './generate_engine_api_key_modal/generate_engine_api_key_modal';
export const EngineAPI: React.FC = () => {
const { engineName, isLoadingEngine } = useValues(EngineViewLogic);
const { engineData } = useValues(EngineIndicesLogic);
export const SearchApplicationAPI = () => {
const { engineName } = useValues(EngineViewLogic);
const { isGenerateModalOpen } = useValues(EngineApiLogic);
const { openGenerateModal, closeGenerateModal } = useActions(EngineApiLogic);
const enterpriseSearchUrl = getEnterpriseSearchUrl();
const { navigateToUrl } = useValues(KibanaLogic);
if (!engineData) return null;
const steps = [
{
title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.title', {
@ -186,22 +178,11 @@ export const EngineAPI: React.FC = () => {
];
return (
<EnterpriseSearchEnginesPageTemplate
pageChrome={[engineName]}
pageViewTelemetry={EngineViewTabs.API}
isLoading={isLoadingEngine}
pageHeader={{
pageTitle: i18n.translate('xpack.enterpriseSearch.content.engine.api.pageTitle', {
defaultMessage: 'API',
}),
rightSideItems: [],
}}
engineName={engineName}
>
<>
{isGenerateModalOpen ? (
<GenerateEngineApiKeyModal engineName={engineName} onClose={closeGenerateModal} />
) : null}
<EuiSteps headingElement="h2" steps={steps} />
</EnterpriseSearchEnginesPageTemplate>
</>
);
};

View file

@ -18,8 +18,11 @@ import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
import { ENGINES_PATH } from '../../routes';
export const EngineError: React.FC<{ error: HttpError | undefined }> = ({ error }) => {
if (error?.body?.statusCode === 404) {
export const EngineError: React.FC<{ error?: HttpError; notFound?: boolean }> = ({
error,
notFound,
}) => {
if (notFound || error?.body?.statusCode === 404) {
return (
<>
<SendEnterpriseSearchTelemetry action="error" metric="not_found" />

View file

@ -39,6 +39,10 @@ import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_temp
import { AddIndicesFlyout } from './add_indices_flyout';
import { EngineIndicesLogic } from './engine_indices_logic';
const pageTitle = i18n.translate('xpack.enterpriseSearch.content.engine.indices.pageTitle', {
defaultMessage: 'Indices',
});
export const EngineIndices: React.FC = () => {
const subduedBackground = useEuiBackgroundColor('subdued');
const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic);
@ -174,13 +178,11 @@ export const EngineIndices: React.FC = () => {
];
return (
<EnterpriseSearchEnginesPageTemplate
pageChrome={[engineName]}
pageChrome={[engineName, pageTitle]}
pageViewTelemetry={EngineViewTabs.INDICES}
isLoading={isLoadingEngine}
pageHeader={{
pageTitle: i18n.translate('xpack.enterpriseSearch.content.engine.indices.pageTitle', {
defaultMessage: 'Indices',
}),
pageTitle,
rightSideItems: [
<EuiButton
data-telemetry-id="entSearchContent-engines-indices-addNewIndices"

View file

@ -153,6 +153,10 @@ const SchemaFieldDetails: React.FC<{ schemaField: SchemaField }> = ({ schemaFiel
);
};
const pageTitle = i18n.translate('xpack.enterpriseSearch.content.engine.schema.pageTitle', {
defaultMessage: 'Schema',
});
export const EngineSchema: React.FC = () => {
const { engineName } = useValues(EngineIndicesLogic);
const [onlyShowConflicts, setOnlyShowConflicts] = useState<boolean>(false);
@ -345,13 +349,11 @@ export const EngineSchema: React.FC = () => {
return (
<EnterpriseSearchEnginesPageTemplate
pageChrome={[engineName]}
pageChrome={[engineName, pageTitle]}
pageViewTelemetry={EngineViewTabs.SCHEMA}
isLoading={isLoadingEngineSchema}
pageHeader={{
pageTitle: i18n.translate('xpack.enterpriseSearch.content.engine.schema.pageTitle', {
defaultMessage: 'Schema',
}),
pageTitle,
}}
engineName={engineName}
>

View file

@ -79,7 +79,7 @@ export const APICallFlyout: React.FC<APICallFlyoutProps> = ({
<EuiLinkTo
to={generateEncodedPath(ENGINE_TAB_PATH, {
engineName,
tabId: EngineViewTabs.API,
tabId: EngineViewTabs.CONNECT,
})}
color="primary"
target="_blank"

View file

@ -87,6 +87,10 @@ class InternalEngineTransporter implements Transporter {
}
}
const pageTitle = i18n.translate('xpack.enterpriseSearch.content.engine.searchPreview.pageTitle', {
defaultMessage: 'Search Preview',
});
export const EngineSearchPreview: React.FC = () => {
const { http } = useValues(HttpLogic);
const [showAPICallFlyout, setShowAPICallFlyout] = useState<boolean>(false);
@ -114,13 +118,11 @@ export const EngineSearchPreview: React.FC = () => {
return (
<EnterpriseSearchEnginesPageTemplate
pageChrome={[engineName]}
pageChrome={[engineName, pageTitle]}
pageViewTelemetry={EngineViewTabs.PREVIEW}
isLoading={isLoadingEngine}
pageHeader={{
pageTitle: i18n.translate('xpack.enterpriseSearch.content.engine.searchPreview.pageTitle', {
defaultMessage: 'Search Preview',
}),
pageTitle,
rightSideItems: [
<>
<EuiButton

View file

@ -6,7 +6,7 @@
*/
import React, { useEffect } from 'react';
import { useParams, Switch } from 'react-router-dom';
import { useParams, Redirect, Switch } from 'react-router-dom';
import { useValues, useActions } from 'kea';
@ -15,29 +15,28 @@ import { Route } from '@kbn/shared-ux-router';
import { Status } from '../../../../../common/types/api';
import { KibanaLogic } from '../../../shared/kibana';
import { ENGINE_PATH, EngineViewTabs } from '../../routes';
import {
ENGINE_PATH,
SEARCH_APPLICATION_CONNECT_PATH,
EngineViewTabs,
SearchApplicationConnectTabs,
} from '../../routes';
import { DeleteEngineModal } from '../engines/delete_engine_modal';
import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template';
import { EngineAPI } from './engine_api/engine_api';
import { EngineConnect } from './engine_connect/engine_connect';
import { EngineError } from './engine_error';
import { EngineIndices } from './engine_indices';
import { EngineSchema } from './engine_schema';
import { EngineSearchPreview } from './engine_search_preview/engine_search_preview';
import { EngineViewHeaderActions } from './engine_view_header_actions';
import { EngineViewLogic } from './engine_view_logic';
import { EngineHeaderDocsAction } from './header_docs_action';
export const EngineView: React.FC = () => {
const { fetchEngine, closeDeleteEngineModal } = useActions(EngineViewLogic);
const {
engineName,
fetchEngineApiError,
fetchEngineApiStatus,
isDeleteModalVisible,
isLoadingEngine,
} = useValues(EngineViewLogic);
const { engineName, fetchEngineApiError, fetchEngineApiStatus, isDeleteModalVisible } =
useValues(EngineViewLogic);
const { tabId = EngineViewTabs.PREVIEW } = useParams<{
tabId?: string;
}>();
@ -77,21 +76,25 @@ export const EngineView: React.FC = () => {
/>
<Route exact path={`${ENGINE_PATH}/${EngineViewTabs.INDICES}`} component={EngineIndices} />
<Route exact path={`${ENGINE_PATH}/${EngineViewTabs.SCHEMA}`} component={EngineSchema} />
<Route exact path={`${ENGINE_PATH}/${EngineViewTabs.API}`} component={EngineAPI} />
<Route // TODO: remove this route when all engine view routes are implemented, replace with a 404 route
render={() => (
<EnterpriseSearchEnginesPageTemplate
pageChrome={[engineName]}
pageViewTelemetry={tabId}
pageHeader={{
pageTitle: tabId,
rightSideItems: [<EngineViewHeaderActions />],
}}
engineName={engineName}
isLoading={isLoadingEngine}
/>
)}
<Route path={SEARCH_APPLICATION_CONNECT_PATH} component={EngineConnect} />
<Redirect
from={`${ENGINE_PATH}/${EngineViewTabs.CONNECT}`}
to={`${ENGINE_PATH}/${EngineViewTabs.CONNECT}/${SearchApplicationConnectTabs.API}`}
/>
<Route>
<EnterpriseSearchEnginesPageTemplate
isEmptyState
pageChrome={[engineName]}
pageViewTelemetry={tabId}
pageHeader={{
pageTitle: engineName,
rightSideItems: [],
}}
engineName={engineName}
>
<EngineError notFound />
</EnterpriseSearchEnginesPageTemplate>
</Route>
</Switch>
</>
);

View file

@ -27,13 +27,17 @@ export const OLD_SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}
export const ENGINES_PATH = `${ROOT_PATH}engines`;
export const ENGINE_CREATION_PATH = `${ENGINES_PATH}/new`;
export const ENGINE_PATH = `${ENGINES_PATH}/:engineName`;
export const ENGINE_TAB_PATH = `${ENGINE_PATH}/:tabId`;
export enum EngineViewTabs {
PREVIEW = 'preview',
INDICES = 'indices',
SCHEMA = 'schema',
CONNECT = 'connect',
}
export const ENGINE_CREATION_PATH = `${ENGINES_PATH}/new`;
export const ENGINE_PATH = `${ENGINES_PATH}/:engineName`;
export const ENGINE_TAB_PATH = `${ENGINE_PATH}/:tabId`;
export const SEARCH_APPLICATION_CONNECT_PATH = `${ENGINE_PATH}/${EngineViewTabs.CONNECT}/:connectTabId`;
export enum SearchApplicationConnectTabs {
API = 'api',
}

View file

@ -10,7 +10,6 @@ import { useValues } from 'kea';
import { EuiBreadcrumb } from '@elastic/eui';
import {
ENGINES_PLUGIN,
ENTERPRISE_SEARCH_OVERVIEW_PLUGIN,
ANALYTICS_PLUGIN,
APP_SEARCH_PLUGIN,
@ -139,7 +138,4 @@ export const useSearchExperiencesBreadcrumbs = (breadcrumbs: Breadcrumbs = []) =
]);
export const useEnterpriseSearchEnginesBreadcrumbs = (breadcrumbs: Breadcrumbs = []) =>
useEnterpriseSearchBreadcrumbs([
{ text: ENGINES_PLUGIN.NAV_TITLE, path: '/engines' },
...breadcrumbs,
]);
useEnterpriseSearchBreadcrumbs(breadcrumbs);

View file

@ -9,6 +9,8 @@ import React, { useEffect } from 'react';
import { useValues } from 'kea';
import { ENGINES_PLUGIN } from '../../../../common/constants';
import { KibanaLogic } from '../kibana';
import {
@ -176,8 +178,9 @@ export const SetEnterpriseSearchEnginesChrome: React.FC<SetChromeProps> = ({ tra
const title = reverseArray(trail);
const docTitle = appSearchTitle(title);
const crumbs = useGenerateBreadcrumbs(trail);
const breadcrumbs = useEnterpriseSearchEnginesBreadcrumbs(crumbs);
const breadcrumbs = useEnterpriseSearchEnginesBreadcrumbs(
useGenerateBreadcrumbs([ENGINES_PLUGIN.NAV_TITLE, ...trail])
);
useEffect(() => {
setBreadcrumbs(breadcrumbs);

View file

@ -300,9 +300,9 @@ describe('useEnterpriseSearchEngineNav', () => {
name: 'Schema',
},
{
href: `/app/enterprise_search/content/engines/${engineName}/api`,
id: 'enterpriseSearchEngineAPI',
name: 'API',
href: `/app/enterprise_search/content/engines/${engineName}/connect`,
id: 'enterpriseSearchApplicationConnect',
name: 'Connect',
},
],
name: engineName,

View file

@ -189,6 +189,7 @@ export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?:
name: engineName,
...generateNavLink({
shouldNotCreateHref: true,
shouldShowActiveForSubroutes: true,
to: enginePath,
}),
items: [
@ -223,13 +224,17 @@ export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?:
}),
},
{
id: 'enterpriseSearchEngineAPI',
name: i18n.translate('xpack.enterpriseSearch.nav.engine.apiTitle', {
defaultMessage: 'API',
}),
id: 'enterpriseSearchApplicationConnect',
name: i18n.translate(
'xpack.enterpriseSearch.nav.applications.searchApplications.connectTitle',
{
defaultMessage: 'Connect',
}
),
...generateNavLink({
shouldNotCreateHref: true,
to: `${enginePath}/${EngineViewTabs.API}`,
shouldShowActiveForSubroutes: true,
to: `${enginePath}/${EngineViewTabs.CONNECT}`,
}),
},
],

View file

@ -12226,7 +12226,6 @@
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.done": "Terminé",
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.generateButton": "Générer une clé en lecture seule",
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title": "Créer une clé d'API en lecture seule pour le moteur",
"xpack.enterpriseSearch.content.engine.api.pageTitle": "API",
"xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning": "Elastic ne stocke pas les clés dAPI. Une fois la clé générée, vous ne pourrez la visualiser qu'une seule fois. Veillez à l'enregistrer dans un endroit sûr. Si vous n'y avez plus accès, vous devrez générer une nouvelle clé dAPI à partir de cet écran.",
"xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton": "Créer une clé d'API",
"xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink": "Découvrez plus d'informations sur les clés d'API.",
@ -13130,7 +13129,6 @@
"xpack.enterpriseSearch.nav.contentSettingsTitle": "Paramètres",
"xpack.enterpriseSearch.nav.contentTitle": "Contenu",
"xpack.enterpriseSearch.nav.elasticsearchTitle": "Elasticsearch",
"xpack.enterpriseSearch.nav.engine.apiTitle": "API",
"xpack.enterpriseSearch.nav.engine.indicesTitle": "Index",
"xpack.enterpriseSearch.nav.engine.schemaTitle": "Schéma",
"xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle": "Aperçu",

View file

@ -12225,7 +12225,6 @@
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.done": "完了",
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.generateButton": "読み取り専用キーを生成",
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title": "エンジン読み取り専用APIキーを作成",
"xpack.enterpriseSearch.content.engine.api.pageTitle": "API",
"xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning": "ElasticはAPIキーを保存しません。生成後は、1回だけキーを表示できます。必ず安全に保管してください。アクセスできなくなった場合は、この画面から新しいAPIキーを生成する必要があります。",
"xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton": "APIキーを作成",
"xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink": "APIキーの詳細をご覧ください。",
@ -13129,7 +13128,6 @@
"xpack.enterpriseSearch.nav.contentSettingsTitle": "設定",
"xpack.enterpriseSearch.nav.contentTitle": "コンテンツ",
"xpack.enterpriseSearch.nav.elasticsearchTitle": "Elasticsearch",
"xpack.enterpriseSearch.nav.engine.apiTitle": "API",
"xpack.enterpriseSearch.nav.engine.indicesTitle": "インデックス",
"xpack.enterpriseSearch.nav.engine.schemaTitle": "スキーマ",
"xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle": "概要",

View file

@ -12226,7 +12226,6 @@
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.done": "完成",
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.generateButton": "生成只读密钥",
"xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title": "创建引擎只读 API 密钥",
"xpack.enterpriseSearch.content.engine.api.pageTitle": "API",
"xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning": "Elastic 不会存储 API 密钥。一旦生成,您只能查看密钥一次。请确保将其保存在某个安全位置。如果失去它的访问权限,您需要从此屏幕生成新的 API 密钥。",
"xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton": "创建 API 密钥",
"xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink": "详细了解 API 密钥。",
@ -13130,7 +13129,6 @@
"xpack.enterpriseSearch.nav.contentSettingsTitle": "设置",
"xpack.enterpriseSearch.nav.contentTitle": "内容",
"xpack.enterpriseSearch.nav.elasticsearchTitle": "Elasticsearch",
"xpack.enterpriseSearch.nav.engine.apiTitle": "API",
"xpack.enterpriseSearch.nav.engine.indicesTitle": "索引",
"xpack.enterpriseSearch.nav.engine.schemaTitle": "架构",
"xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle": "概览",