[Enterprise Search] New SearchIndex view utilizing EuiTabbedContent (#135362)

This commit is contained in:
Byron Hulcher 2022-06-29 09:58:33 -04:00 committed by GitHub
parent 2dee998df0
commit 617ff01f7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 267 additions and 240 deletions

View file

@ -7,8 +7,11 @@
import { kea, MakeLogicType } from 'kea';
import { generateEncodedPath } from '../../../app_search/utils/encode_path_params';
import { KibanaLogic } from '../../../shared/kibana';
import { SEARCH_INDEX_CONFIGURATION_PATH } from '../../routes';
import { SearchIndexTabId } from '../../components/search_index/search_index';
import { SEARCH_INDEX_TAB_PATH } from '../../routes';
import { AddConnectorPackageApiLogic } from './add_connector_package_api_logic';
@ -23,7 +26,10 @@ export const AddConnectorPackageLogic = kea<MakeLogicType<{}, AddConnectorAction
listeners: {
apiSuccess: ({ indexName }) => {
KibanaLogic.values.navigateToUrl(
SEARCH_INDEX_CONFIGURATION_PATH.replace(':indexSlug', indexName)
generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
indexName,
tabId: SearchIndexTabId.CONFIGURATION,
})
);
},
},

View file

@ -32,8 +32,13 @@ export interface Connector {
sync_status: string | null;
}
export interface Crawler {
domains: [];
}
export interface IndexData {
connector?: Connector;
crawler?: Crawler;
index: {
aliases: string[];
health: string;

View file

@ -7,6 +7,8 @@
import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useActions, useValues } from 'kea';
import {
@ -21,20 +23,22 @@ import {
import { i18n } from '@kbn/i18n';
import { GenerateConnectorApiKeyApiLogic } from '../../api/connector_package/generate_connector_api_key_api_logic';
import { FetchIndexApiLogic } from '../../api/index/fetch_index_api_logic';
import { ApiKey } from '../api_key/api_key';
export const ConfigurationConnector: React.FC<{ indexId: string; indexName: string }> = ({
indexId,
indexName,
}) => {
export const ConfigurationConnector: React.FC = () => {
const { data: indexData } = useValues(FetchIndexApiLogic);
const { makeRequest, apiReset } = useActions(GenerateConnectorApiKeyApiLogic);
const { data: apiKeyData } = useValues(GenerateConnectorApiKeyApiLogic);
const { indexSlug: indexName } = useParams<{ indexSlug: string }>();
const indexId = indexData?.connector?.id ?? '';
useEffect(() => {
apiReset();
return apiReset;
}, [indexName]);
// TODO If index && !index.connector then do something
return (
<EuiFlexGroup direction="column">
<EuiFlexItem>

View file

@ -7,16 +7,6 @@
import React from 'react';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
export const SearchIndexDocuments: React.FC = () => {
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[]}
pageViewTelemetry="Documents"
isLoading={false}
>
<>Documents</>
</EnterpriseSearchContentPageTemplate>
);
return <>Documents</>;
};

View file

@ -0,0 +1,14 @@
/*
* 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';
export const SearchIndexDomainManagement: React.FC = () => {
// TODO If index && !index.crawler then do something
return <>Manage Domains</>;
};

View file

@ -5,4 +5,8 @@
* 2.0.
*/
export { SearchIndexRouter } from './search_index_router';
import React from 'react';
export const SearchIndexIndexMappings: React.FC = () => {
return <>Index Mappings</>;
};

View file

@ -15,15 +15,10 @@ import { i18n } from '@kbn/i18n';
import { generateNavLink } from '../../../shared/layout';
import {
SEARCH_INDEX_PATH,
SEARCH_INDEX_OVERVIEW_PATH,
SEARCH_INDEX_DOCUMENTS_PATH,
SEARCH_INDEX_SCHEMA_PATH,
SEARCH_INDEX_LOGS_PATH,
} from '../../routes';
import { SEARCH_INDEX_PATH, SEARCH_INDEX_TAB_PATH } from '../../routes';
import './index_nav.scss';
import { SearchIndexTabId } from './search_index';
// TODO: replace once logic in place.
const indexName = 'Index name goes here';
@ -46,37 +41,39 @@ export const useSearchIndicesNav = () => {
'data-test-subj': 'IndexLabel',
},
{
id: 'overview',
id: SearchIndexTabId.OVERVIEW,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.nav.overviewTitle', {
defaultMessage: 'Overview',
}),
...generateNavLink({ to: generatePath(SEARCH_INDEX_OVERVIEW_PATH, { indexSlug }) }),
...generateNavLink({
to: generatePath(SEARCH_INDEX_TAB_PATH, { indexSlug, tabId: SearchIndexTabId.OVERVIEW }),
}),
'data-test-subj': 'IndexOverviewLink',
},
{
id: 'documents',
id: SearchIndexTabId.DOCUMENTS,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.nav.documentsTitle', {
defaultMessage: 'Documents',
}),
...generateNavLink({ to: generatePath(SEARCH_INDEX_DOCUMENTS_PATH, { indexSlug }) }),
...generateNavLink({
to: generatePath(SEARCH_INDEX_TAB_PATH, { indexSlug, tabId: SearchIndexTabId.DOCUMENTS }),
}),
'data-test-subj': 'IndexDocumentsLink',
},
{
id: 'schema',
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.nav.schemaTitle', {
defaultMessage: 'Schema',
id: SearchIndexTabId.INDEX_MAPPINGS,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.nav.indexMappingsTitle', {
defaultMessage: 'Index Mappings',
}),
...generateNavLink({ to: generatePath(SEARCH_INDEX_SCHEMA_PATH, { indexSlug }) }),
'data-test-subj': 'IndexSchemaLink',
},
{
id: 'logs',
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.nav.logsitle', {
defaultMessage: 'Logs',
...generateNavLink({
to: generatePath(SEARCH_INDEX_TAB_PATH, {
indexSlug,
tabId: SearchIndexTabId.INDEX_MAPPINGS,
}),
}),
...generateNavLink({ to: generatePath(SEARCH_INDEX_LOGS_PATH, { indexSlug }) }),
'data-test-subj': 'IndexLogsLink',
'data-test-subj': 'IndexIndexMappingsLink',
},
// TODO Conditionally display links for connector/crawler
];
return navItems;

View file

@ -1,18 +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 React from 'react';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
export const SearchIndexLogs: React.FC = () => {
return (
<EnterpriseSearchContentPageTemplate pageChrome={[]} pageViewTelemetry="Logs" isLoading={false}>
<>Logs</>
</EnterpriseSearchContentPageTemplate>
);
};

View file

@ -5,11 +5,9 @@
* 2.0.
*/
import React, { useEffect, useState } from 'react';
import React from 'react';
import { useParams } from 'react-router-dom';
import { useActions, useValues } from 'kea';
import { useValues } from 'kea';
import {
EuiCodeBlock,
@ -19,111 +17,69 @@ import {
EuiButtonIcon,
EuiFlexItem,
EuiPanel,
EuiTabs,
EuiTab,
EuiSpacer,
} from '@elastic/eui';
import { Status } from '../../../../../common/types/api';
import { getEnterpriseSearchUrl } from '../../../shared/enterprise_search_url/external_url';
import { FetchIndexApiLogic } from '../../api/index/fetch_index_api_logic';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
import { DOCUMENTS_API_JSON_EXAMPLE } from '../new_index/constants';
import { ConfigurationConnector } from './configuration_connector';
import { TotalStats } from './total_stats';
interface SearchIndexOverviewProps {
navTabIndex?: number;
}
export const SearchIndexOverview: React.FC<SearchIndexOverviewProps> = ({ navTabIndex }) => {
const [tabIndex, setSelectedTabIndex] = useState(navTabIndex ?? 0);
const { makeRequest, apiReset } = useActions(FetchIndexApiLogic);
export const SearchIndexOverview: React.FC = () => {
const { data, status } = useValues(FetchIndexApiLogic);
const { indexSlug: indexName } = useParams<{ indexSlug: string }>();
useEffect(() => {
makeRequest({ indexName });
return apiReset;
}, [indexName]);
const searchIndexApiUrl = getEnterpriseSearchUrl('/api/ent/v1/search_indices/');
const apiKey = 'Create an API Key';
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[]}
pageViewTelemetry="Overview"
isLoading={false}
pageHeader={{ pageTitle: indexName }}
>
<EuiTabs bottomBorder expand size="xl">
<EuiTab isSelected={tabIndex === 0} onClick={() => setSelectedTabIndex(0)}>
Overview
</EuiTab>
<EuiTab isSelected={tabIndex === 1} onClick={() => setSelectedTabIndex(1)}>
Document explorer
</EuiTab>
<EuiTab isSelected={tabIndex === 2} onClick={() => setSelectedTabIndex(2)}>
Index mappings
</EuiTab>
<EuiTab isSelected={tabIndex === 3} onClick={() => setSelectedTabIndex(3)}>
Configuration
</EuiTab>
</EuiTabs>
<EuiSpacer size="l" />
<>
{status === Status.SUCCESS && data && (
<TotalStats
lastUpdated={'TODO'}
documentCount={data.index.total.docs.count ?? 0}
indexHealth={data.index.health ?? ''}
ingestionType={data.connector ? 'Connector' : 'API'}
ingestionType={data.connector ? 'Connector' : data.crawler ? 'Crawler' : 'API'}
/>
)}
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel>
{tabIndex === 0 && (
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<EuiText>
<h2>Indexing by API</h2>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup justifyContent="flexEnd" alignItems="center">
<EuiFlexItem>
<EuiButtonIcon iconType="iInCircle" />
</EuiFlexItem>
<EuiFlexItem>
<EuiButton>Generate an API key</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiCodeBlock language="bash" fontSize="m" isCopyable>
{`\
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<EuiText>
<h2>Indexing by API</h2>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup justifyContent="flexEnd" alignItems="center">
<EuiFlexItem>
<EuiButtonIcon iconType="iInCircle" />
</EuiFlexItem>
<EuiFlexItem>
<EuiButton>Generate an API key</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiCodeBlock language="bash" fontSize="m" isCopyable>
{`\
curl -X POST '${searchIndexApiUrl}${name}/document' \\
-H 'Content-Type: application/json' \\
-H 'Authorization: Bearer ${apiKey}' \\
-d '${JSON.stringify(DOCUMENTS_API_JSON_EXAMPLE, null, 2)}'
`}
</EuiCodeBlock>
</EuiFlexItem>
</EuiFlexGroup>
)}
{tabIndex === 3 && (
<ConfigurationConnector indexName={indexName} indexId={data?.connector?.id ?? ''} />
)}
</EuiCodeBlock>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</EnterpriseSearchContentPageTemplate>
</>
);
};

View file

@ -0,0 +1,14 @@
/*
* 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';
export const SearchIndexScheduling: React.FC = () => {
// TODO If index && !index.connector then do something
return <>Scheduling</>;
};

View file

@ -1,22 +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 React from 'react';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
export const SearchIndexSchema: React.FC = () => {
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[]}
pageViewTelemetry="Schema"
isLoading={false}
>
<>Schema</>
</EnterpriseSearchContentPageTemplate>
);
};

View file

@ -0,0 +1,141 @@
/*
* 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, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useActions, useValues } from 'kea';
import { EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Status } from '../../../../../common/types/api';
import { generateEncodedPath } from '../../../app_search/utils/encode_path_params';
import { KibanaLogic } from '../../../shared/kibana';
import { FetchIndexApiLogic } from '../../api/index/fetch_index_api_logic';
import { SEARCH_INDEX_PATH, SEARCH_INDEX_TAB_PATH } from '../../routes';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
import { baseBreadcrumbs } from '../search_indices';
import { ConfigurationConnector } from './configuration_connector';
import { SearchIndexDocuments } from './documents';
import { SearchIndexDomainManagement } from './domain_management';
import { SearchIndexIndexMappings } from './index_mappings';
import { SearchIndexOverview } from './overview';
import { SearchIndexScheduling } from './scheduling';
export enum SearchIndexTabId {
// all indices
OVERVIEW = 'overview',
DOCUMENTS = 'documents',
INDEX_MAPPINGS = 'index_mappings',
// connector indices
CONFIGURATION = 'configuration',
SCHEDULING = 'scheduling',
// crawler indices
DOMAIN_MANAGEMENT = 'domain_management',
}
export const SearchIndex: React.FC = () => {
const { makeRequest, apiReset } = useActions(FetchIndexApiLogic);
const { data: indexData, status: indexApiStatus } = useValues(FetchIndexApiLogic);
const { indexName, tabId = SearchIndexTabId.OVERVIEW } = useParams<{
indexName: string;
tabId?: string;
}>();
useEffect(() => {
makeRequest({ indexName });
return apiReset;
}, [indexName]);
const ALL_INDICES_TABS: EuiTabbedContentTab[] = [
{
content: <SearchIndexOverview />,
id: SearchIndexTabId.OVERVIEW,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.overviewTabLabel', {
defaultMessage: 'Overview',
}),
},
{
content: <SearchIndexDocuments />,
id: SearchIndexTabId.DOCUMENTS,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.documentsTabLabel', {
defaultMessage: 'Documents',
}),
},
{
content: <SearchIndexIndexMappings />,
id: SearchIndexTabId.INDEX_MAPPINGS,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.indexMappingsTabLabel', {
defaultMessage: 'Index Mappings',
}),
},
];
const CONNECTOR_TABS: EuiTabbedContentTab[] = [
{
content: <ConfigurationConnector />,
id: SearchIndexTabId.CONFIGURATION,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.configurationTabLabel', {
defaultMessage: 'Configuration',
}),
},
{
content: <SearchIndexScheduling />,
id: SearchIndexTabId.SCHEDULING,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.schedulingTabLabel', {
defaultMessage: 'Scheduling',
}),
},
];
const CRAWLER_TABS: EuiTabbedContentTab[] = [
{
content: <SearchIndexDomainManagement />,
id: SearchIndexTabId.DOMAIN_MANAGEMENT,
name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.domainManagementTabLabel', {
defaultMessage: 'Manage Domains',
}),
},
];
const tabs: EuiTabbedContentTab[] = [
...ALL_INDICES_TABS,
...(indexData?.connector ? CONNECTOR_TABS : []),
...(indexData?.crawler ? CRAWLER_TABS : []),
];
const selectedTab = tabs.find((tab) => tab.id === tabId);
const onTabClick = (tab: EuiTabbedContentTab) => {
KibanaLogic.values.navigateToUrl(
generateEncodedPath(
tab.id === SearchIndexTabId.OVERVIEW ? SEARCH_INDEX_PATH : SEARCH_INDEX_TAB_PATH,
{
indexName,
tabId: tab.id,
}
)
);
};
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[...baseBreadcrumbs, indexName]}
pageViewTelemetry={tabId}
isLoading={indexApiStatus === Status.LOADING || indexApiStatus === Status.IDLE}
pageHeader={{ pageTitle: indexName }}
>
<EuiTabbedContent tabs={tabs} selectedTab={selectedTab} onTabClick={onTabClick} />
</EnterpriseSearchContentPageTemplate>
);
};

View file

@ -1,24 +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 '../../../__mocks__/react_router';
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { shallow } from 'enzyme';
import { SearchIndexRouter } from '.';
describe('SearchIndexRouter', () => {
it('renders Search index routes', () => {
const wrapper = shallow(<SearchIndexRouter />);
expect(wrapper.find(Switch)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(5);
});
});

View file

@ -1,44 +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 React from 'react';
import { Route, Switch } from 'react-router-dom';
import {
SEARCH_INDEX_OVERVIEW_PATH,
SEARCH_INDEX_DOCUMENTS_PATH,
SEARCH_INDEX_SCHEMA_PATH,
SEARCH_INDEX_LOGS_PATH,
SEARCH_INDEX_CONFIGURATION_PATH,
} from '../../routes';
import { SearchIndexDocuments } from './documents';
import { SearchIndexLogs } from './logs';
import { SearchIndexOverview } from './overview';
import { SearchIndexSchema } from './schema';
export const SearchIndexRouter: React.FC = () => {
return (
<Switch>
<Route exact path={SEARCH_INDEX_OVERVIEW_PATH}>
<SearchIndexOverview />
</Route>
<Route exact path={SEARCH_INDEX_DOCUMENTS_PATH}>
<SearchIndexDocuments />
</Route>
<Route exact path={SEARCH_INDEX_SCHEMA_PATH}>
<SearchIndexSchema />
</Route>
<Route exact path={SEARCH_INDEX_LOGS_PATH}>
<SearchIndexLogs />
</Route>
<Route exact path={SEARCH_INDEX_CONFIGURATION_PATH}>
<SearchIndexOverview navTabIndex={3} />
</Route>
</Switch>
);
};

View file

@ -27,7 +27,7 @@ import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'
import { GettingStartedSteps } from '../../../shared/getting_started_steps';
import { EuiLinkTo, EuiButtonIconTo } from '../../../shared/react_router_helpers';
import { SEARCH_INDEX_OVERVIEW_PATH, NEW_INDEX_PATH } from '../../routes';
import { NEW_INDEX_PATH, SEARCH_INDEX_PATH } from '../../routes';
import { SearchIndex } from '../../types';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
@ -71,7 +71,7 @@ export const SearchIndices: React.FC = () => {
render: (name: string, { indexSlug }: SearchIndex) => (
<EuiLinkTo
data-test-subj="search-index-link"
to={generatePath(SEARCH_INDEX_OVERVIEW_PATH, { indexSlug })}
to={generatePath(SEARCH_INDEX_PATH, { indexSlug })}
>
{name}
</EuiLinkTo>
@ -125,7 +125,7 @@ export const SearchIndices: React.FC = () => {
<EuiButtonIconTo
iconType="eye"
data-test-subj="view-search-index-button"
to={generatePath(SEARCH_INDEX_OVERVIEW_PATH, { indexSlug })}
to={generatePath(SEARCH_INDEX_PATH, { indexSlug })}
/>
),
},

View file

@ -19,6 +19,6 @@ describe('SearchIndicesRouter', () => {
const wrapper = shallow(<SearchIndicesRouter />);
expect(wrapper.find(Switch)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(3);
expect(wrapper.find(Route)).toHaveLength(4);
});
});

View file

@ -8,10 +8,15 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { SEARCH_INDICES_PATH, SEARCH_INDEX_PATH, NEW_INDEX_PATH } from '../../routes';
import {
SEARCH_INDICES_PATH,
SEARCH_INDEX_PATH,
SEARCH_INDEX_TAB_PATH,
NEW_INDEX_PATH,
} from '../../routes';
import { NewIndex } from '../new_index';
import { SearchIndexRouter } from '../search_index';
import { SearchIndex } from '../search_index/search_index';
import { SearchIndices } from './search_indices';
@ -24,8 +29,11 @@ export const SearchIndicesRouter: React.FC = () => {
<Route exact path={SEARCH_INDICES_PATH}>
<SearchIndices />
</Route>
<Route path={SEARCH_INDEX_PATH}>
<SearchIndexRouter />
<Route path={SEARCH_INDEX_PATH} exact>
<SearchIndex />
</Route>
<Route path={SEARCH_INDEX_TAB_PATH}>
<SearchIndex />
</Route>
</Switch>
);

View file

@ -18,9 +18,5 @@ export const NEW_API_PATH = `${NEW_INDEX_PATH}/api`;
export const NEW_ES_INDEX_PATH = `${NEW_INDEX_PATH}/elasticsearch`;
export const NEW_DIRECT_UPLOAD_PATH = `${NEW_INDEX_PATH}/upload`;
export const SEARCH_INDEX_PATH = `${SEARCH_INDICES_PATH}/:indexSlug`;
export const SEARCH_INDEX_OVERVIEW_PATH = `${SEARCH_INDEX_PATH}/`;
export const SEARCH_INDEX_CONFIGURATION_PATH = `${SEARCH_INDEX_PATH}/configuration`;
export const SEARCH_INDEX_DOCUMENTS_PATH = `${SEARCH_INDEX_PATH}/documents`;
export const SEARCH_INDEX_SCHEMA_PATH = `${SEARCH_INDEX_PATH}/schema`;
export const SEARCH_INDEX_LOGS_PATH = `${SEARCH_INDEX_PATH}/logs`;
export const SEARCH_INDEX_PATH = `${SEARCH_INDICES_PATH}/:indexName`;
export const SEARCH_INDEX_TAB_PATH = `${SEARCH_INDEX_PATH}/:tabId`;