Skip to content

Commit

Permalink
[Enterprise Search] De-couple Overview from ent-search (#161995)
Browse files Browse the repository at this point in the history
## Summary

Updated the Overview page to render an connection error callout instead
of the full ErrorConnecting page. This allows us to render the
Elasticsearch product card even without ent-search available.

This required removing the "Insufficient permissions" view since we also
always want to show the elasticsearch product card even if the user
doesn't have access to app search and workplace search.

### Screenshots
<img width="1530" alt="image"
src="https://github.com/elastic/kibana/assets/1972968/2ad5b905-3f42-435b-81e5-a7d71ce8039e">
  • Loading branch information
TattdCodeMonkey authored Jul 18, 2023
1 parent fde21b1 commit b5965a3
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 171 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import React from 'react';

import { shallow } from 'enzyme';

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

import { WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants';
import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt';
import { ErrorStateCallout } from '../../../shared/error_state';

import { ProductCard } from '../product_card';
import { SetupGuideCta } from '../setup_guide';
Expand Down Expand Up @@ -51,6 +51,40 @@ describe('ProductSelector', () => {
);
});

it('does not render connection error callout without an error', () => {
setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } });
const wrapper = shallow(<ProductSelector {...props} />);

expect(wrapper.find(ErrorStateCallout)).toHaveLength(0);
});

it('does render connection error callout with an error', () => {
setMockValues({
config: { canDeployEntSearch: true, host: 'localhost' },
errorConnectingMessage: '502 Bad Gateway',
});
const wrapper = shallow(<ProductSelector {...props} />);

expect(wrapper.find(ErrorStateCallout)).toHaveLength(1);
});

it('renders add content', () => {
setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } });
const wrapper = shallow(<ProductSelector {...props} />);

expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(1);
});

it('does not render add content when theres a connection error', () => {
setMockValues({
config: { canDeployEntSearch: true, host: 'localhost' },
errorConnectingMessage: '502 Bad Gateway',
});
const wrapper = shallow(<ProductSelector {...props} />);

expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(0);
});

describe('access checks when host is set', () => {
beforeEach(() => {
setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } });
Expand Down Expand Up @@ -82,11 +116,11 @@ describe('ProductSelector', () => {
expect(wrapper.find('[data-test-subj="productCard-elasticsearch"]')).toHaveLength(1);
});

it('renders empty prompt and no cards or license callout if the user does not have access', () => {
it('renders elasticsearch card if the user does not have access app search & workplace search', () => {
const wrapper = shallow(<ProductSelector {...props} />);

expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1);
expect(wrapper.find(ProductCard)).toHaveLength(0);
expect(wrapper.find(ProductCard)).toHaveLength(1);
expect(wrapper.find('[data-test-subj="productCard-elasticsearch"]')).toHaveLength(1);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,7 @@ import React from 'react';

import { useValues } from 'kea';

import {
EuiButton,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiImage,
EuiLink,
EuiSpacer,
EuiTitle,
} from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
import { Chat } from '@kbn/cloud-chat-plugin/public';
import { i18n } from '@kbn/i18n';

Expand All @@ -29,6 +20,8 @@ import {
} from '../../../../../common/constants';
import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt';
import { docLinks } from '../../../shared/doc_links';
import { ErrorStateCallout } from '../../../shared/error_state';
import { HttpLogic } from '../../../shared/http';
import { KibanaLogic } from '../../../shared/kibana';
import { SetSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
Expand All @@ -38,8 +31,6 @@ import { ProductCard } from '../product_card';
import { SetupGuideCta } from '../setup_guide';
import { TrialCallout } from '../trial_callout';

import illustration from './lock_light.svg';

interface ProductSelectorProps {
access: {
hasAppSearchAccess?: boolean;
Expand All @@ -54,32 +45,53 @@ export const ProductSelector: React.FC<ProductSelectorProps> = ({
}) => {
const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access;
const { config } = useValues(KibanaLogic);
const { errorConnectingMessage } = useValues(HttpLogic);

const showErrorConnecting = !!(config.host && errorConnectingMessage);
// The create index flow does not work without ent-search, when content is updated
// to no longer rely on ent-search we can always show the Add Content component
const showAddContent = config.host && !errorConnectingMessage;

// If Enterprise Search hasn't been set up yet, show all products. Otherwise, only show products the user has access to
const shouldShowAppSearchCard = (!config.host && config.canDeployEntSearch) || hasAppSearchAccess;
const shouldShowWorkplaceSearchCard =
(!config.host && config.canDeployEntSearch) || hasWorkplaceSearchAccess;

// If Enterprise Search has been set up and the user does not have access to either product, show a message saying they
// need to contact an administrator to get access to one of the products.
const shouldShowEnterpriseSearchCards =
shouldShowAppSearchCard || shouldShowWorkplaceSearchCard || !config.canDeployEntSearch;

const WORKPLACE_SEARCH_URL = isWorkplaceSearchAdmin
? WORKPLACE_SEARCH_PLUGIN.URL
: WORKPLACE_SEARCH_PLUGIN.NON_ADMIN_URL;

const productCards = (
<>
<AddContentEmptyPrompt
title={i18n.translate('xpack.enterpriseSearch.overview.emptyPromptTitle', {
defaultMessage: 'Add data and start searching',
})}
buttonLabel={i18n.translate('xpack.enterpriseSearch.overview.emptyPromptButtonLabel', {
defaultMessage: 'Create an Elasticsearch index',
})}
/>
<EuiSpacer size="xxl" />
return (
<EnterpriseSearchOverviewPageTemplate
restrictWidth
pageHeader={{
pageTitle: i18n.translate('xpack.enterpriseSearch.overview.pageTitle', {
defaultMessage: 'Welcome to Search',
}),
}}
>
<SetPageChrome />
<SendTelemetry action="viewed" metric="overview" />
<TrialCallout />
{showAddContent && (
<>
<AddContentEmptyPrompt
title={i18n.translate('xpack.enterpriseSearch.overview.emptyPromptTitle', {
defaultMessage: 'Add data and start searching',
})}
buttonLabel={i18n.translate('xpack.enterpriseSearch.overview.emptyPromptButtonLabel', {
defaultMessage: 'Create an Elasticsearch index',
})}
/>
<EuiSpacer size="xxl" />
</>
)}
{showErrorConnecting && (
<>
<SendTelemetry action="error" metric="cannot_connect" />
<ErrorStateCallout />
</>
)}
<EuiSpacer size="xxl" />
<EuiTitle>
<h3>
Expand Down Expand Up @@ -305,72 +317,6 @@ export const ProductSelector: React.FC<ProductSelectorProps> = ({
</EuiFlexItem>
)}
</EuiFlexGroup>
</>
);

const insufficientAccessMessage = (
<EuiEmptyPrompt
icon={<EuiImage size="fullWidth" src={illustration} alt="" />}
title={
<h2>
{i18n.translate('xpack.enterpriseSearch.overview.insufficientPermissionsTitle', {
defaultMessage: 'Insufficient permissions',
})}
</h2>
}
layout="horizontal"
color="plain"
body={
<>
<p>
{i18n.translate('xpack.enterpriseSearch.overview.insufficientPermissionsBody', {
defaultMessage:
'You don’t have access to view this page. If you feel this may be an error, please contact your administrator.',
})}
</p>
</>
}
actions={
<EuiButton color="primary" fill href="/">
{i18n.translate('xpack.enterpriseSearch.overview.insufficientPermissionsButtonLabel', {
defaultMessage: 'Go to the Kibana dashboard',
})}
</EuiButton>
}
footer={
<>
<EuiTitle size="xxs">
<span>
{i18n.translate('xpack.enterpriseSearch.overview.insufficientPermissionsFooterBody', {
defaultMessage: 'Go to the Kibana dashboard',
})}
</span>
</EuiTitle>{' '}
<EuiLink href={docLinks.kibanaSecurity} target="_blank">
{i18n.translate(
'xpack.enterpriseSearch.overview.insufficientPermissionsFooterLinkLabel',
{
defaultMessage: 'Read documentation',
}
)}
</EuiLink>
</>
}
/>
);
return (
<EnterpriseSearchOverviewPageTemplate
restrictWidth
pageHeader={{
pageTitle: i18n.translate('xpack.enterpriseSearch.overview.pageTitle', {
defaultMessage: 'Welcome to Enterprise Search',
}),
}}
>
<SetPageChrome />
<SendTelemetry action="viewed" metric="overview" />
<TrialCallout />
{shouldShowEnterpriseSearchCards ? productCards : insufficientAccessMessage}
<Chat />
</EnterpriseSearchOverviewPageTemplate>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import React from 'react';
import { shallow } from 'enzyme';

import { VersionMismatchPage } from '../shared/version_mismatch';
import { rerender } from '../test_helpers';

import { ErrorConnecting } from './components/error_connecting';
import { ProductSelector } from './components/product_selector';
import { SetupGuide } from './components/setup_guide';

Expand All @@ -32,29 +30,6 @@ describe('EnterpriseSearchOverview', () => {
expect(wrapper.find(ProductSelector)).toHaveLength(1);
});

it('renders the error connecting prompt only if host is configured', () => {
setMockValues({
errorConnectingMessage: '502 Bad Gateway',
config: { host: 'localhost' },
});
const wrapper = shallow(<EnterpriseSearchOverview />);

expect(wrapper.find(VersionMismatchPage)).toHaveLength(0);
const errorConnecting = wrapper.find(ErrorConnecting);
expect(errorConnecting).toHaveLength(1);
expect(wrapper.find(ProductSelector)).toHaveLength(0);

setMockValues({
errorConnectingMessage: '502 Bad Gateway',
config: { host: '' },
});
rerender(wrapper);

expect(wrapper.find(VersionMismatchPage)).toHaveLength(0);
expect(wrapper.find(ErrorConnecting)).toHaveLength(0);
expect(wrapper.find(ProductSelector)).toHaveLength(1);
});

it('renders the version error message if versions mismatch and the host is configured', () => {
setMockValues({
errorConnectingMessage: '',
Expand All @@ -65,7 +40,6 @@ describe('EnterpriseSearchOverview', () => {
);

expect(wrapper.find(VersionMismatchPage)).toHaveLength(1);
expect(wrapper.find(ErrorConnecting)).toHaveLength(0);
expect(wrapper.find(ProductSelector)).toHaveLength(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ import { Routes, Route } from '@kbn/shared-ux-router';

import { isVersionMismatch } from '../../../common/is_version_mismatch';
import { InitialAppData } from '../../../common/types';
import { HttpLogic } from '../shared/http';
import { KibanaLogic } from '../shared/kibana';
import { VersionMismatchPage } from '../shared/version_mismatch';

import { ErrorConnecting } from './components/error_connecting';
import { ProductSelector } from './components/product_selector';
import { SetupGuide } from './components/setup_guide';
import { ROOT_PATH, SETUP_GUIDE_PATH } from './routes';
Expand All @@ -28,10 +26,8 @@ export const EnterpriseSearchOverview: React.FC<InitialAppData> = ({
enterpriseSearchVersion,
kibanaVersion,
}) => {
const { errorConnectingMessage } = useValues(HttpLogic);
const { config } = useValues(KibanaLogic);

const showErrorConnecting = !!(config.host && errorConnectingMessage);
const incompatibleVersions = !!(
config.host && isVersionMismatch(enterpriseSearchVersion, kibanaVersion)
);
Expand All @@ -45,8 +41,6 @@ export const EnterpriseSearchOverview: React.FC<InitialAppData> = ({
kibanaVersion={kibanaVersion}
/>
);
} else if (showErrorConnecting) {
return <ErrorConnecting />;
}

return <ProductSelector isWorkplaceSearchAdmin={isWorkplaceSearchAdmin} access={access} />;
Expand Down
Loading

0 comments on commit b5965a3

Please sign in to comment.