Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] [Onboarding] Add mappings component to index details page (#193314) #193578

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x-pack/plugins/search_indices/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
],
"requiredPlugins": [
"share",
"indexManagement",
],
"optionalPlugins": [
"cloud",
Expand Down
5 changes: 1 addition & 4 deletions x-pack/plugins/search_indices/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { I18nProvider } from '@kbn/i18n-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import { Router } from '@kbn/shared-ux-router';
import { UsageTrackerContextProvider } from './contexts/usage_tracker_context';
import { SearchIndicesServicesContextDeps } from './types';

Expand All @@ -30,9 +29,7 @@ export const renderApp = async (
<UsageTrackerContextProvider usageCollection={services.usageCollection}>
<I18nProvider>
<QueryClientProvider client={queryClient}>
<Router history={services.history}>
<App />
</Router>
<App />
</QueryClientProvider>
</I18nProvider>
</UsageTrackerContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
EuiButton,
EuiPageTemplate,
EuiFlexItem,
EuiTabbedContent,
EuiFlexGroup,
EuiPopover,
EuiButtonIcon,
Expand All @@ -19,8 +18,10 @@ import {
EuiText,
EuiIcon,
EuiButtonEmpty,
EuiTabbedContent,
EuiTabbedContentTab,
} from '@elastic/eui';
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
Expand All @@ -33,18 +34,56 @@ import { useIndexMapping } from '../../hooks/api/use_index_mappings';
import { IndexDocuments } from '../index_documents/index_documents';
import { DeleteIndexModal } from './delete_index_modal';
import { IndexloadingError } from './details_page_loading_error';
import { SearchIndicesDetailsMappingsTabs } from '../../routes';
import { SearchIndexDetailsMappings } from './details_page_mappings';

export const SearchIndexDetailsPage = () => {
const indexName = decodeURIComponent(useParams<{ indexName: string }>().indexName);
const { console: consolePlugin, docLinks, application } = useKibana().services;
const tabId = decodeURIComponent(useParams<{ tabId: string }>().tabId);

const { console: consolePlugin, docLinks, application, history } = useKibana().services;
const { data: index, refetch, isError: isIndexError, isInitialLoading } = useIndex(indexName);
const {
data: mappings,
isError: isMappingsError,
isInitialLoading: isMappingsInitialLoading,
} = useIndexMapping(indexName);

const SearchIndexDetailsTabs: EuiTabbedContentTab[] = useMemo(() => {
return [
{
id: SearchIndicesDetailsMappingsTabs.DATA,
name: i18n.translate('xpack.searchIndices.documentsTabLabel', {
defaultMessage: 'Data',
}),
content: <IndexDocuments indexName={indexName} />,
'data-test-subj': `${SearchIndicesDetailsMappingsTabs.DATA}Tab`,
},
{
id: SearchIndicesDetailsMappingsTabs.MAPPINGS,
name: i18n.translate('xpack.searchIndices.mappingsTabLabel', {
defaultMessage: 'Mappings',
}),
content: <SearchIndexDetailsMappings index={index} />,
'data-test-subj': `${SearchIndicesDetailsMappingsTabs.MAPPINGS}Tab`,
},
];
}, [index, indexName]);

const [selectedTab, setSelectedTab] = useState(SearchIndexDetailsTabs[0]);

useEffect(() => {
const newTab = SearchIndexDetailsTabs.find((tab) => tab.id === tabId);
if (newTab) setSelectedTab(newTab);
}, [SearchIndexDetailsTabs, tabId]);

const handleTabClick = useCallback(
(tab) => {
history.push(`index_details/${indexName}/${tab.id}`);
},

[history, indexName]
);
const embeddableConsole = useMemo(
() => (consolePlugin?.EmbeddableConsole ? <consolePlugin.EmbeddableConsole /> : null),
[consolePlugin]
Expand Down Expand Up @@ -176,15 +215,9 @@ export const SearchIndexDetailsPage = () => {
<EuiFlexItem>
<EuiFlexItem>
<EuiTabbedContent
tabs={[
{
id: 'data',
name: i18n.translate('xpack.searchIndices.documentsTabLabel', {
defaultMessage: 'Data',
}),
content: <IndexDocuments indexName={indexName} />,
},
]}
tabs={SearchIndexDetailsTabs}
onTabClick={handleTabClick}
selectedTab={selectedTab}
/>
</EuiFlexItem>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { EuiSpacer } from '@elastic/eui';
import { Index } from '@kbn/index-management-shared-types';
import React from 'react';
import { useMemo } from 'react';
import { useKibana } from '../../hooks/use_kibana';
export interface SearchIndexDetailsMappingsProps {
index?: Index;
}
export const SearchIndexDetailsMappings = ({ index }: SearchIndexDetailsMappingsProps) => {
const { indexManagement, history } = useKibana().services;

const IndexMappingComponent = useMemo(
() => indexManagement.getIndexMappingComponent({ history }),
[indexManagement, history]
);

return (
<>
<EuiSpacer />
<IndexMappingComponent index={index} showAboutMappings={false} />
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,32 @@
* 2.0.
*/
import React from 'react';
import { Route, Routes } from '@kbn/shared-ux-router';
import { SEARCH_INDICES_DETAILS_PATH } from '../../routes';
import { SearchIndexDetailsPage } from './details_page';
import { Route, Router, Routes } from '@kbn/shared-ux-router';
import { Redirect } from 'react-router-dom';
import { useKibana } from '../../hooks/use_kibana';

import {
SearchIndicesDetailsMappingsTabs,
SEARCH_INDICES_DETAILS_PATH,
SEARCH_INDICES_DETAILS_TABS_PATH,
} from '../../routes';
import { SearchIndexDetailsPage } from './details_page';
export const SearchIndicesRouter: React.FC = () => {
const { application } = useKibana().services;
const { application, history } = useKibana().services;
return (
<Routes>
<Route exact path={SEARCH_INDICES_DETAILS_PATH} component={SearchIndexDetailsPage} />
<Route render={() => application.navigateToApp('elasticsearchStart')} />
</Routes>
<Router history={history}>
<Routes>
<Route exact path={[SEARCH_INDICES_DETAILS_TABS_PATH, SEARCH_INDICES_DETAILS_PATH]}>
<Routes>
<Route path={SEARCH_INDICES_DETAILS_TABS_PATH} component={SearchIndexDetailsPage} />
<Redirect
exact
from={`${SEARCH_INDICES_DETAILS_PATH}/`}
to={`${SEARCH_INDICES_DETAILS_PATH}/${SearchIndicesDetailsMappingsTabs.DATA}`}
/>
</Routes>
</Route>
<Route render={() => application.navigateToApp('elasticsearchStart')} />
</Routes>
</Router>
);
};
5 changes: 5 additions & 0 deletions x-pack/plugins/search_indices/public/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@

export const ROOT_PATH = '/';
export const SEARCH_INDICES_DETAILS_PATH = `${ROOT_PATH}index_details/:indexName`;
export const SEARCH_INDICES_DETAILS_TABS_PATH = `${SEARCH_INDICES_DETAILS_PATH}/:tabId`;
export enum SearchIndicesDetailsMappingsTabs {
DATA = 'data',
MAPPINGS = 'mappings',
}
2 changes: 2 additions & 0 deletions x-pack/plugins/search_indices/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import type { MappingPropertyBase } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { IndexManagementPluginStart } from '@kbn/index-management-shared-types';

export interface SearchIndicesPluginSetup {
enabled: boolean;
Expand All @@ -37,6 +38,7 @@ export interface SearchIndicesServicesContextDeps {
export type SearchIndicesServicesContext = CoreStart &
SearchIndicesAppPluginStartDependencies & {
history: AppMountParameters['history'];
indexManagement: IndexManagementPluginStart;
};

export interface AppUsageTracker {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont
const retry = getService('retry');

return {
async expectToBeIndexDetailPage() {
expect(await browser.getCurrentUrl()).contain('/index_details');
},
async expectIndexDetailPageHeader() {
await testSubjects.existOrFail('searchIndexDetailsHeader', { timeout: 2000 });
},
Expand Down Expand Up @@ -111,5 +108,18 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont
await testSubjects.click('reloadButton', 2000);
});
},
async expectWithDataTabsExists() {
await testSubjects.existOrFail('mappingsTab', { timeout: 2000 });
await testSubjects.existOrFail('dataTab', { timeout: 2000 });
},
async expectShouldDefaultToDataTab() {
expect(await browser.getCurrentUrl()).contain('/data');
},
async withDataChangeTabs(tab: 'dataTab' | 'mappingsTab') {
await testSubjects.click(tab);
},
async expectUrlShouldChangeTo(tab: 'data' | 'mappings') {
expect(await browser.getCurrentUrl()).contain(`/${tab}`);
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,34 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await pageObjects.svlSearchIndexDetailPage.expectAddDocumentCodeExamples();
});

it('should have index documents', async () => {
await es.index({
index: indexName,
body: {
my_field: [1, 0, 1],
},
});

await svlSearchNavigation.navigateToIndexDetailPage(indexName);
await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments();
});

it('back to indices button should redirect to list page', async () => {
await pageObjects.svlSearchIndexDetailPage.expectBackToIndicesButtonExists();
await pageObjects.svlSearchIndexDetailPage.clickBackToIndicesButton();
await pageObjects.svlSearchIndexDetailPage.expectBackToIndicesButtonRedirectsToListPage();
});
describe('With data', () => {
before(async () => {
await svlSearchNavigation.navigateToIndexDetailPage(indexName);
await es.index({
index: indexName,
body: {
my_field: [1, 0, 1],
},
});
});
it('should have index documents', async () => {
await svlSearchNavigation.navigateToIndexDetailPage(indexName);
await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments();
});
it('should have with data tabs', async () => {
await pageObjects.svlSearchIndexDetailPage.expectWithDataTabsExists();
await pageObjects.svlSearchIndexDetailPage.expectShouldDefaultToDataTab();
});
it('should be able to change tabs', async () => {
await pageObjects.svlSearchIndexDetailPage.withDataChangeTabs('mappingsTab');
await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('mappings');
});
});

describe('page loading error', () => {
before(async () => {
Expand Down