From d67a421e68508aed1413750c2e1696d0d9921f39 Mon Sep 17 00:00:00 2001 From: Constance Date: Wed, 9 Sep 2020 12:53:51 -0700 Subject: [PATCH] [Enterprise Search] Add Overview landing page/plugin (#76734) * [public] Register Enterprise search plugin + move new Home strings to constants * [server] Register plugin access/visibility * Set up Enterprise Search Kibana Chrome - Add SetEnterpriseSearchChrome - Update Enterprise Search breadcrumbs to link to new overview plugin (+ update overview plugin URL per team discussion) - Add ability to break out of React Router basename by not using history.createhref - Update createHref mock to more closely match Kibana urls (adding /app prefix) - Minor documentation fix * Set up Enterprise Search plugin telemetry - client-side: SendEnterpriseSearchTelemetry - server-side: register saved objects, usage collector, etc. * Enterprise search overview views (#23) * Add formatTestSubj util This allows us to correctly format strings into our casing for data-test-subj attrs * Add images and stylesheet * Add product card component * Add index component * Remove unused styles * Fix inter-plugin links - by add shouldNotCreateHref prop to RR helpers - similiar to breadcrumb change * Fix/clean up CSS - Prefer EUI components over bespoke CSS (e.g. EuiCard) - Remove unused or unspecific CSS - Pull out product card CSS to its component - Fix kebab-cased CSS classes to camelCased * Clean up ProductCard props - Prefer passing in our plugin consts instead of separate props - Move productCardDescription to constants - Update tests * Add telemetry clicked actions to product buttons + revert data-test-subj strings to previous implementation + prune format_test_subj helper by using lodash util directly * [PR feedback] Add new plugin to applicationUsageSchema per telemetry team request * Fix failing functional navLinks test * Fix telemetry schema test * [Perf] Optimize assets size by switching from 300kb SVG to 25kb PNG * Only show product cards if the user has access to that product - adds access checks - fixes flex/CSS to show one card at a time Co-authored-by: Scotty Bollinger --- .../collectors/application_usage/schema.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 28 ++++++ .../enterprise_search/common/constants.ts | 30 +++++- .../enterprise_search/common/types/index.ts | 4 + .../__mocks__/react_router_history.mock.ts | 2 +- .../enterprise_search/assets/app_search.png | Bin 0 -> 25568 bytes .../assets/bg_enterprise_search.png | Bin 0 -> 24757 bytes .../assets/workplace_search.png | Bin 0 -> 30483 bytes .../components/product_card/index.ts | 7 ++ .../components/product_card/product_card.scss | 58 ++++++++++++ .../product_card/product_card.test.tsx | 57 ++++++++++++ .../components/product_card/product_card.tsx | 71 ++++++++++++++ .../applications/enterprise_search/index.scss | 54 +++++++++++ .../enterprise_search/index.test.tsx | 50 ++++++++++ .../applications/enterprise_search/index.tsx | 78 ++++++++++++++++ .../generate_breadcrumbs.test.ts | 54 +++++++---- .../kibana_chrome/generate_breadcrumbs.ts | 16 +++- .../shared/kibana_chrome/generate_title.ts | 2 +- .../shared/kibana_chrome/index.ts | 6 +- .../shared/kibana_chrome/set_chrome.test.tsx | 33 ++++++- .../shared/kibana_chrome/set_chrome.tsx | 26 +++++- .../react_router_helpers/eui_link.test.tsx | 10 +- .../shared/react_router_helpers/eui_link.tsx | 18 +++- .../applications/shared/telemetry/index.ts | 7 +- .../shared/telemetry/send_telemetry.test.tsx | 22 ++++- .../shared/telemetry/send_telemetry.tsx | 14 ++- .../enterprise_search/public/plugin.ts | 38 ++++---- .../enterprise_search/telemetry.test.ts | 85 +++++++++++++++++ .../collectors/enterprise_search/telemetry.ts | 87 ++++++++++++++++++ .../server/collectors/lib/telemetry.test.ts | 2 +- .../enterprise_search/server/plugin.ts | 18 +++- .../routes/enterprise_search/telemetry.ts | 3 +- .../enterprise_search/telemetry.ts | 19 ++++ .../schema/xpack_plugins.json | 21 +++++ .../security_only/tests/nav_links.ts | 8 +- 35 files changed, 868 insertions(+), 61 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/assets/app_search.png create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/assets/bg_enterprise_search.png create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/assets/workplace_search.png create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx create mode 100644 x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.test.ts create mode 100644 x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.ts create mode 100644 x-pack/plugins/enterprise_search/server/saved_objects/enterprise_search/telemetry.ts diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index 6efe8725535838..2e79cdaa7fc6bd 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -66,6 +66,7 @@ export const applicationUsageSchema = { csm: commonSchema, canvas: commonSchema, dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it + enterpriseSearch: commonSchema, appSearch: commonSchema, workplaceSearch: commonSchema, graph: commonSchema, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index acd575badbe5b8..5bce03a2927604 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -414,6 +414,34 @@ } } }, + "enterpriseSearch": { + "properties": { + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + } + } + }, "appSearch": { "properties": { "clicks_total": { diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 05d27d7337a6ef..6e2f0c0f24b7a4 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -11,7 +11,24 @@ export const ENTERPRISE_SEARCH_PLUGIN = { NAME: i18n.translate('xpack.enterpriseSearch.productName', { defaultMessage: 'Enterprise Search', }), - URL: '/app/enterprise_search', + NAV_TITLE: i18n.translate('xpack.enterpriseSearch.navTitle', { + defaultMessage: 'Overview', + }), + SUBTITLE: i18n.translate('xpack.enterpriseSearch.featureCatalogue.subtitle', { + defaultMessage: 'Search everything', + }), + DESCRIPTIONS: [ + i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription1', { + defaultMessage: 'Build a powerful search experience.', + }), + i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription2', { + defaultMessage: 'Connect your users to relevant data.', + }), + i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription3', { + defaultMessage: 'Unify your team content.', + }), + ], + URL: '/app/enterprise_search/overview', }; export const APP_SEARCH_PLUGIN = { @@ -23,6 +40,10 @@ export const APP_SEARCH_PLUGIN = { defaultMessage: 'Leverage dashboards, analytics, and APIs for advanced application search made simple.', }), + CARD_DESCRIPTION: i18n.translate('xpack.enterpriseSearch.appSearch.productCardDescription', { + defaultMessage: + 'Elastic App Search provides user-friendly tools to design and deploy a powerful search to your websites or web/mobile applications.', + }), URL: '/app/enterprise_search/app_search', SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/app-search/', }; @@ -36,6 +57,13 @@ export const WORKPLACE_SEARCH_PLUGIN = { defaultMessage: 'Search all documents, files, and sources available across your virtual workplace.', }), + CARD_DESCRIPTION: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.productCardDescription', + { + defaultMessage: + "Unify all your team's content in one place, with instant connectivity to popular productivity and collaboration tools.", + } + ), URL: '/app/enterprise_search/workplace_search', SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/workplace-search/', }; diff --git a/x-pack/plugins/enterprise_search/common/types/index.ts b/x-pack/plugins/enterprise_search/common/types/index.ts index a41a42da477eef..d5774adc0d516c 100644 --- a/x-pack/plugins/enterprise_search/common/types/index.ts +++ b/x-pack/plugins/enterprise_search/common/types/index.ts @@ -18,6 +18,10 @@ export interface IInitialAppData { ilmEnabled?: boolean; isFederatedAuth?: boolean; configuredLimits?: IConfiguredLimits; + access?: { + hasAppSearchAccess: boolean; + hasWorkplaceSearchAccess: boolean; + }; appSearch?: IAppSearchAccount; workplaceSearch?: IWorkplaceSearchInitialData; } diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts index 779eb1a043e8c8..842dcefd3aef8a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts @@ -9,7 +9,7 @@ * Jest to accept its use within a jest.mock() */ export const mockHistory = { - createHref: jest.fn(({ pathname }) => `/enterprise_search${pathname}`), + createHref: jest.fn(({ pathname }) => `/app/enterprise_search${pathname}`), push: jest.fn(), location: { pathname: '/current-path', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/assets/app_search.png b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/assets/app_search.png new file mode 100644 index 0000000000000000000000000000000000000000..6cf0639167e2fe45a58adfebe78c4864ac336e3a GIT binary patch literal 25568 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 literal 0 HcmV?d00001 diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/assets/bg_enterprise_search.png b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/assets/bg_enterprise_search.png new file mode 100644 index 0000000000000000000000000000000000000000..1b5e1e489fd96c8a764a2e2802f42a1d77272ee7 GIT binary patch literal 24757 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 literal 0 HcmV?d00001 diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/index.ts new file mode 100644 index 00000000000000..df85a10f7e9de6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ProductCard } from './product_card'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss new file mode 100644 index 00000000000000..d6b6bd34425906 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.scss @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +.productCard { + margin: $euiSizeS; + + &__imageContainer { + max-height: 115px; + overflow: hidden; + background-color: #0076cc; + + @include euiBreakpoint('s', 'm', 'l', 'xl') { + max-height: none; + } + } + + &__image { + width: 100%; + height: auto; + } + + .euiCard__content { + max-width: 350px; + margin-top: $euiSizeL; + + @include euiBreakpoint('s', 'm', 'l', 'xl') { + margin-top: $euiSizeXL; + } + } + + .euiCard__title { + margin-bottom: $euiSizeM; + font-weight: $euiFontWeightBold; + + @include euiBreakpoint('s', 'm', 'l', 'xl') { + margin-bottom: $euiSizeL; + font-size: $euiSizeL; + } + } + + .euiCard__description { + font-weight: $euiFontWeightMedium; + color: $euiColorMediumShade; + margin-bottom: $euiSize; + } + + .euiCard__footer { + margin-bottom: $euiSizeS; + + @include euiBreakpoint('s', 'm', 'l', 'xl') { + margin-bottom: $euiSizeM; + font-size: $euiSizeL; + } + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx new file mode 100644 index 00000000000000..a76b654ccddd06 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiCard } from '@elastic/eui'; +import { EuiButton } from '../../../shared/react_router_helpers'; +import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; + +jest.mock('../../../shared/telemetry', () => ({ + sendTelemetry: jest.fn(), +})); +import { sendTelemetry } from '../../../shared/telemetry'; + +import { ProductCard } from './'; + +describe('ProductCard', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders an App Search card', () => { + const wrapper = shallow(); + const card = wrapper.find(EuiCard).dive().shallow(); + + expect(card.find('h2').text()).toEqual('Elastic App Search'); + expect(card.find('.productCard__image').prop('src')).toEqual('as.jpg'); + + const button = card.find(EuiButton); + expect(button.prop('to')).toEqual('/app/enterprise_search/app_search'); + expect(button.prop('data-test-subj')).toEqual('LaunchAppSearchButton'); + + button.simulate('click'); + expect(sendTelemetry).toHaveBeenCalledWith(expect.objectContaining({ metric: 'app_search' })); + }); + + it('renders a Workplace Search card', () => { + const wrapper = shallow(); + const card = wrapper.find(EuiCard).dive().shallow(); + + expect(card.find('h2').text()).toEqual('Elastic Workplace Search'); + expect(card.find('.productCard__image').prop('src')).toEqual('ws.jpg'); + + const button = card.find(EuiButton); + expect(button.prop('to')).toEqual('/app/enterprise_search/workplace_search'); + expect(button.prop('data-test-subj')).toEqual('LaunchWorkplaceSearchButton'); + + button.simulate('click'); + expect(sendTelemetry).toHaveBeenCalledWith( + expect.objectContaining({ metric: 'workplace_search' }) + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx new file mode 100644 index 00000000000000..334ca126cabb9d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext } from 'react'; +import upperFirst from 'lodash/upperFirst'; +import snakeCase from 'lodash/snakeCase'; +import { i18n } from '@kbn/i18n'; +import { EuiCard, EuiTextColor } from '@elastic/eui'; + +import { EuiButton } from '../../../shared/react_router_helpers'; +import { sendTelemetry } from '../../../shared/telemetry'; +import { KibanaContext, IKibanaContext } from '../../../index'; + +import './product_card.scss'; + +interface IProductCard { + // Expects product plugin constants (@see common/constants.ts) + product: { + ID: string; + NAME: string; + CARD_DESCRIPTION: string; + URL: string; + }; + image: string; +} + +export const ProductCard: React.FC = ({ product, image }) => { + const { http } = useContext(KibanaContext) as IKibanaContext; + + return ( + + + + } + paddingSize="l" + description={{product.CARD_DESCRIPTION}} + footer={ + + sendTelemetry({ + http, + product: 'enterprise_search', + action: 'clicked', + metric: snakeCase(product.ID), + }) + } + data-test-subj={`Launch${upperFirst(product.ID)}Button`} + > + {i18n.translate('xpack.enterpriseSearch.overview.productCard.button', { + defaultMessage: `Launch {productName}`, + values: { productName: product.NAME }, + })} + + } + /> + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss new file mode 100644 index 00000000000000..d9379433523178 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +.enterpriseSearchOverview { + padding-top: 78px; + background-image: url('./assets/bg_enterprise_search.png'); + background-repeat: no-repeat; + background-size: 670px; + background-position: center -27px; + + @include euiBreakpoint('m', 'l', 'xl') { + padding-top: 158px; + background-size: 1160px; + background-position: center -48px; + } + + &__header { + text-align: center; + margin: auto; + } + + &__heading { + @include euiBreakpoint('xs', 's') { + font-size: $euiFontSizeXL; + line-height: map-get(map-get($euiTitles, 'm'), 'line-height'); + } + } + + &__subheading { + color: $euiColorMediumShade; + font-size: $euiFontSize; + + @include euiBreakpoint('m', 'l', 'xl') { + font-size: $euiFontSizeL; + margin-bottom: $euiSizeL; + } + } + + // EUI override + .euiTitle + .euiTitle { + margin-top: 0; + + @include euiBreakpoint('m', 'l', 'xl') { + margin-top: $euiSizeS; + } + } + + .enterpriseSearchOverview__card { + flex-basis: 50%; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx new file mode 100644 index 00000000000000..cd2a22a45bbb4c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiPage } from '@elastic/eui'; + +import { EnterpriseSearch } from './'; +import { ProductCard } from './components/product_card'; + +describe('EnterpriseSearch', () => { + it('renders the overview page and product cards', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(EuiPage).hasClass('enterpriseSearchOverview')).toBe(true); + expect(wrapper.find(ProductCard)).toHaveLength(2); + }); + + describe('access checks', () => { + it('does not render the App Search card if the user does not have access to AS', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('product').ID).toEqual('workplaceSearch'); + }); + + it('does not render the Workplace Search card if the user does not have access to WS', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('product').ID).toEqual('appSearch'); + }); + + it('does not render any cards if the user does not have access', () => { + const wrapper = shallow(); + + expect(wrapper.find(ProductCard)).toHaveLength(0); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx new file mode 100644 index 00000000000000..373f595a6a9ea5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { + EuiPage, + EuiPageBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPageContentBody, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { IInitialAppData } from '../../../common/types'; +import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../common/constants'; + +import { SetEnterpriseSearchChrome as SetPageChrome } from '../shared/kibana_chrome'; +import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../shared/telemetry'; + +import { ProductCard } from './components/product_card'; + +import AppSearchImage from './assets/app_search.png'; +import WorkplaceSearchImage from './assets/workplace_search.png'; +import './index.scss'; + +export const EnterpriseSearch: React.FC = ({ access = {} }) => { + const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access; + + return ( + + + + + + + + +

+ {i18n.translate('xpack.enterpriseSearch.overview.heading', { + defaultMessage: 'Welcome to Elastic Enterprise Search', + })} +

+ + +

+ {i18n.translate('xpack.enterpriseSearch.overview.subheading', { + defaultMessage: 'Select a product to get started', + })} +

+
+ + + + + {hasAppSearchAccess && ( + + + + )} + {hasWorkplaceSearchAccess && ( + + + + )} + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts index 9e86b239432a7d..3c8b3a72188627 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts @@ -37,27 +37,37 @@ describe('useBreadcrumbs', () => { expect(breadcrumb).toEqual([ { text: 'Hello', - href: '/enterprise_search/hello', + href: '/app/enterprise_search/hello', onClick: expect.any(Function), }, { text: 'World', - href: '/enterprise_search/world', + href: '/app/enterprise_search/world', onClick: expect.any(Function), }, ]); }); it('prevents default navigation and uses React Router history on click', () => { - const breadcrumb = useBreadcrumbs([{ text: '', path: '/' }])[0] as any; + const breadcrumb = useBreadcrumbs([{ text: '', path: '/test' }])[0] as any; const event = { preventDefault: jest.fn() }; breadcrumb.onClick(event); - expect(mockKibanaContext.navigateToUrl).toHaveBeenCalled(); + expect(mockKibanaContext.navigateToUrl).toHaveBeenCalledWith('/app/enterprise_search/test'); expect(mockHistory.createHref).toHaveBeenCalled(); expect(event.preventDefault).toHaveBeenCalled(); }); + it('does not call createHref if shouldNotCreateHref is passed', () => { + const breadcrumb = useBreadcrumbs([ + { text: '', path: '/test', shouldNotCreateHref: true }, + ])[0] as any; + breadcrumb.onClick({ preventDefault: () => null }); + + expect(mockKibanaContext.navigateToUrl).toHaveBeenCalledWith('/test'); + expect(mockHistory.createHref).not.toHaveBeenCalled(); + }); + it('does not prevent default browser behavior on new tab/window clicks', () => { const breadcrumb = useBreadcrumbs([{ text: '', path: '/' }])[0] as any; @@ -95,15 +105,17 @@ describe('useEnterpriseSearchBreadcrumbs', () => { expect(useEnterpriseSearchBreadcrumbs(breadcrumbs)).toEqual([ { text: 'Enterprise Search', + href: '/app/enterprise_search/overview', + onClick: expect.any(Function), }, { text: 'Page 1', - href: '/enterprise_search/page1', + href: '/app/enterprise_search/page1', onClick: expect.any(Function), }, { text: 'Page 2', - href: '/enterprise_search/page2', + href: '/app/enterprise_search/page2', onClick: expect.any(Function), }, ]); @@ -113,6 +125,8 @@ describe('useEnterpriseSearchBreadcrumbs', () => { expect(useEnterpriseSearchBreadcrumbs()).toEqual([ { text: 'Enterprise Search', + href: '/app/enterprise_search/overview', + onClick: expect.any(Function), }, ]); }); @@ -122,7 +136,7 @@ describe('useAppSearchBreadcrumbs', () => { beforeEach(() => { jest.clearAllMocks(); mockHistory.createHref.mockImplementation( - ({ pathname }: any) => `/enterprise_search/app_search${pathname}` + ({ pathname }: any) => `/app/enterprise_search/app_search${pathname}` ); }); @@ -141,20 +155,22 @@ describe('useAppSearchBreadcrumbs', () => { expect(useAppSearchBreadcrumbs(breadcrumbs)).toEqual([ { text: 'Enterprise Search', + href: '/app/enterprise_search/overview', + onClick: expect.any(Function), }, { text: 'App Search', - href: '/enterprise_search/app_search/', + href: '/app/enterprise_search/app_search/', onClick: expect.any(Function), }, { text: 'Page 1', - href: '/enterprise_search/app_search/page1', + href: '/app/enterprise_search/app_search/page1', onClick: expect.any(Function), }, { text: 'Page 2', - href: '/enterprise_search/app_search/page2', + href: '/app/enterprise_search/app_search/page2', onClick: expect.any(Function), }, ]); @@ -164,10 +180,12 @@ describe('useAppSearchBreadcrumbs', () => { expect(useAppSearchBreadcrumbs()).toEqual([ { text: 'Enterprise Search', + href: '/app/enterprise_search/overview', + onClick: expect.any(Function), }, { text: 'App Search', - href: '/enterprise_search/app_search/', + href: '/app/enterprise_search/app_search/', onClick: expect.any(Function), }, ]); @@ -178,7 +196,7 @@ describe('useWorkplaceSearchBreadcrumbs', () => { beforeEach(() => { jest.clearAllMocks(); mockHistory.createHref.mockImplementation( - ({ pathname }: any) => `/enterprise_search/workplace_search${pathname}` + ({ pathname }: any) => `/app/enterprise_search/workplace_search${pathname}` ); }); @@ -197,20 +215,22 @@ describe('useWorkplaceSearchBreadcrumbs', () => { expect(useWorkplaceSearchBreadcrumbs(breadcrumbs)).toEqual([ { text: 'Enterprise Search', + href: '/app/enterprise_search/overview', + onClick: expect.any(Function), }, { text: 'Workplace Search', - href: '/enterprise_search/workplace_search/', + href: '/app/enterprise_search/workplace_search/', onClick: expect.any(Function), }, { text: 'Page 1', - href: '/enterprise_search/workplace_search/page1', + href: '/app/enterprise_search/workplace_search/page1', onClick: expect.any(Function), }, { text: 'Page 2', - href: '/enterprise_search/workplace_search/page2', + href: '/app/enterprise_search/workplace_search/page2', onClick: expect.any(Function), }, ]); @@ -220,10 +240,12 @@ describe('useWorkplaceSearchBreadcrumbs', () => { expect(useWorkplaceSearchBreadcrumbs()).toEqual([ { text: 'Enterprise Search', + href: '/app/enterprise_search/overview', + onClick: expect.any(Function), }, { text: 'Workplace Search', - href: '/enterprise_search/workplace_search/', + href: '/app/enterprise_search/workplace_search/', onClick: expect.any(Function), }, ]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts index 6eab936719d014..19714608e73e9b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts @@ -26,6 +26,9 @@ import { letBrowserHandleEvent } from '../react_router_helpers'; interface IBreadcrumb { text: string; path?: string; + // Used to navigate outside of the React Router basename, + // i.e. if we need to go from App Search to Enterprise Search + shouldNotCreateHref?: boolean; } export type TBreadcrumbs = IBreadcrumb[]; @@ -33,11 +36,11 @@ export const useBreadcrumbs = (breadcrumbs: TBreadcrumbs) => { const history = useHistory(); const { navigateToUrl } = useContext(KibanaContext) as IKibanaContext; - return breadcrumbs.map(({ text, path }) => { + return breadcrumbs.map(({ text, path, shouldNotCreateHref }) => { const breadcrumb = { text } as EuiBreadcrumb; if (path) { - const href = history.createHref({ pathname: path }) as string; + const href = shouldNotCreateHref ? path : (history.createHref({ pathname: path }) as string); breadcrumb.href = href; breadcrumb.onClick = (event) => { @@ -56,7 +59,14 @@ export const useBreadcrumbs = (breadcrumbs: TBreadcrumbs) => { */ export const useEnterpriseSearchBreadcrumbs = (breadcrumbs: TBreadcrumbs = []) => - useBreadcrumbs([{ text: ENTERPRISE_SEARCH_PLUGIN.NAME }, ...breadcrumbs]); + useBreadcrumbs([ + { + text: ENTERPRISE_SEARCH_PLUGIN.NAME, + path: ENTERPRISE_SEARCH_PLUGIN.URL, + shouldNotCreateHref: true, + }, + ...breadcrumbs, + ]); export const useAppSearchBreadcrumbs = (breadcrumbs: TBreadcrumbs = []) => useEnterpriseSearchBreadcrumbs([{ text: APP_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts index 706baefc00cc2f..de5f72de791925 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts @@ -20,7 +20,7 @@ export type TTitle = string[]; /** * Given an array of page titles, return a final formatted document title * @param pages - e.g., ['Curations', 'some Engine', 'App Search'] - * @returns - e.g., 'Curations | some Engine | App Search' + * @returns - e.g., 'Curations - some Engine - App Search' */ export const generateTitle = (pages: TTitle) => pages.join(' - '); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts index 4468d11ba94c94..02013a03c3395a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts @@ -4,4 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export { SetAppSearchChrome, SetWorkplaceSearchChrome } from './set_chrome'; +export { + SetEnterpriseSearchChrome, + SetAppSearchChrome, + SetWorkplaceSearchChrome, +} from './set_chrome'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx index bda816c9a55546..61a066bb92216f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx @@ -12,18 +12,24 @@ import React from 'react'; import { mockKibanaContext, mountWithKibanaContext } from '../../__mocks__'; jest.mock('./generate_breadcrumbs', () => ({ + useEnterpriseSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), useAppSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), useWorkplaceSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), })); -import { useAppSearchBreadcrumbs, useWorkplaceSearchBreadcrumbs } from './generate_breadcrumbs'; +import { + useEnterpriseSearchBreadcrumbs, + useAppSearchBreadcrumbs, + useWorkplaceSearchBreadcrumbs, +} from './generate_breadcrumbs'; jest.mock('./generate_title', () => ({ + enterpriseSearchTitle: jest.fn((title: any) => title), appSearchTitle: jest.fn((title: any) => title), workplaceSearchTitle: jest.fn((title: any) => title), })); -import { appSearchTitle, workplaceSearchTitle } from './generate_title'; +import { enterpriseSearchTitle, appSearchTitle, workplaceSearchTitle } from './generate_title'; -import { SetAppSearchChrome, SetWorkplaceSearchChrome } from './'; +import { SetEnterpriseSearchChrome, SetAppSearchChrome, SetWorkplaceSearchChrome } from './'; describe('Set Kibana Chrome helpers', () => { beforeEach(() => { @@ -35,6 +41,27 @@ describe('Set Kibana Chrome helpers', () => { expect(mockKibanaContext.setDocTitle).toHaveBeenCalled(); }); + describe('SetEnterpriseSearchChrome', () => { + it('sets breadcrumbs and document title', () => { + mountWithKibanaContext(); + + expect(enterpriseSearchTitle).toHaveBeenCalledWith(['Hello World']); + expect(useEnterpriseSearchBreadcrumbs).toHaveBeenCalledWith([ + { + text: 'Hello World', + path: '/current-path', + }, + ]); + }); + + it('sets empty breadcrumbs and document title when isRoot is true', () => { + mountWithKibanaContext(); + + expect(enterpriseSearchTitle).toHaveBeenCalledWith([]); + expect(useEnterpriseSearchBreadcrumbs).toHaveBeenCalledWith([]); + }); + }); + describe('SetAppSearchChrome', () => { it('sets breadcrumbs and document title', () => { mountWithKibanaContext(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx index 43db93c1583d12..5e8d972e1a1355 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx @@ -10,11 +10,17 @@ import { EuiBreadcrumb } from '@elastic/eui'; import { KibanaContext, IKibanaContext } from '../../index'; import { + useEnterpriseSearchBreadcrumbs, useAppSearchBreadcrumbs, useWorkplaceSearchBreadcrumbs, TBreadcrumbs, } from './generate_breadcrumbs'; -import { appSearchTitle, workplaceSearchTitle, TTitle } from './generate_title'; +import { + enterpriseSearchTitle, + appSearchTitle, + workplaceSearchTitle, + TTitle, +} from './generate_title'; /** * Helpers for setting Kibana chrome (breadcrumbs, doc titles) on React view mount @@ -33,6 +39,24 @@ interface IRootBreadcrumbsProps { } type TBreadcrumbsProps = IBreadcrumbsProps | IRootBreadcrumbsProps; +export const SetEnterpriseSearchChrome: React.FC = ({ text, isRoot }) => { + const history = useHistory(); + const { setBreadcrumbs, setDocTitle } = useContext(KibanaContext) as IKibanaContext; + + const title = isRoot ? [] : [text]; + const docTitle = enterpriseSearchTitle(title as TTitle | []); + + const crumb = isRoot ? [] : [{ text, path: history.location.pathname }]; + const breadcrumbs = useEnterpriseSearchBreadcrumbs(crumb as TBreadcrumbs | []); + + useEffect(() => { + setBreadcrumbs(breadcrumbs); + setDocTitle(docTitle); + }, []); + + return null; +}; + export const SetAppSearchChrome: React.FC = ({ text, isRoot }) => { const history = useHistory(); const { setBreadcrumbs, setDocTitle } = useContext(KibanaContext) as IKibanaContext; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx index 063118f94cd191..0c7bac99085dd5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx @@ -45,10 +45,18 @@ describe('EUI & React Router Component Helpers', () => { const link = wrapper.find(EuiLink); expect(link.prop('onClick')).toBeInstanceOf(Function); - expect(link.prop('href')).toEqual('/enterprise_search/foo/bar'); + expect(link.prop('href')).toEqual('/app/enterprise_search/foo/bar'); expect(mockHistory.createHref).toHaveBeenCalled(); }); + it('renders with the correct non-basenamed href when shouldNotCreateHref is passed', () => { + const wrapper = mount(); + const link = wrapper.find(EuiLink); + + expect(link.prop('href')).toEqual('/foo/bar'); + expect(mockHistory.createHref).not.toHaveBeenCalled(); + }); + describe('onClick', () => { it('prevents default navigation and uses React Router history', () => { const wrapper = mount(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx index 7221a61d0997b0..e3b46632ddf9e6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx @@ -21,14 +21,22 @@ import { letBrowserHandleEvent } from './link_events'; interface IEuiReactRouterProps { to: string; onClick?(): void; + // Used to navigate outside of the React Router plugin basename but still within Kibana, + // e.g. if we need to go from Enterprise Search to App Search + shouldNotCreateHref?: boolean; } -export const EuiReactRouterHelper: React.FC = ({ to, onClick, children }) => { +export const EuiReactRouterHelper: React.FC = ({ + to, + onClick, + shouldNotCreateHref, + children, +}) => { const history = useHistory(); const { navigateToUrl } = useContext(KibanaContext) as IKibanaContext; // Generate the correct link href (with basename etc. accounted for) - const href = history.createHref({ pathname: to }); + const href = shouldNotCreateHref ? to : history.createHref({ pathname: to }); const reactRouterLinkClick = (event: React.MouseEvent) => { if (onClick) onClick(); // Run any passed click events (e.g. telemetry) @@ -51,9 +59,10 @@ type TEuiReactRouterButtonProps = EuiButtonProps & IEuiReactRouterProps; export const EuiReactRouterLink: React.FC = ({ to, onClick, + shouldNotCreateHref, ...rest }) => ( - + ); @@ -61,9 +70,10 @@ export const EuiReactRouterLink: React.FC = ({ export const EuiReactRouterButton: React.FC = ({ to, onClick, + shouldNotCreateHref, ...rest }) => ( - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/index.ts index eadf7fa8055906..a8b9636c3ff3e2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/index.ts @@ -5,5 +5,8 @@ */ export { sendTelemetry } from './send_telemetry'; -export { SendAppSearchTelemetry } from './send_telemetry'; -export { SendWorkplaceSearchTelemetry } from './send_telemetry'; +export { + SendEnterpriseSearchTelemetry, + SendAppSearchTelemetry, + SendWorkplaceSearchTelemetry, +} from './send_telemetry'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx index 3c873dbc25e377..8f7cf090e2d573 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx @@ -10,7 +10,12 @@ import { httpServiceMock } from 'src/core/public/mocks'; import { JSON_HEADER as headers } from '../../../../common/constants'; import { mountWithKibanaContext } from '../../__mocks__'; -import { sendTelemetry, SendAppSearchTelemetry, SendWorkplaceSearchTelemetry } from './'; +import { + sendTelemetry, + SendEnterpriseSearchTelemetry, + SendAppSearchTelemetry, + SendWorkplaceSearchTelemetry, +} from './'; describe('Shared Telemetry Helpers', () => { const httpMock = httpServiceMock.createSetupContract(); @@ -44,6 +49,17 @@ describe('Shared Telemetry Helpers', () => { }); describe('React component helpers', () => { + it('SendEnterpriseSearchTelemetry component', () => { + mountWithKibanaContext(, { + http: httpMock, + }); + + expect(httpMock.put).toHaveBeenCalledWith('/api/enterprise_search/telemetry', { + headers, + body: '{"product":"enterprise_search","action":"viewed","metric":"page"}', + }); + }); + it('SendAppSearchTelemetry component', () => { mountWithKibanaContext(, { http: httpMock, @@ -56,13 +72,13 @@ describe('Shared Telemetry Helpers', () => { }); it('SendWorkplaceSearchTelemetry component', () => { - mountWithKibanaContext(, { + mountWithKibanaContext(, { http: httpMock, }); expect(httpMock.put).toHaveBeenCalledWith('/api/enterprise_search/telemetry', { headers, - body: '{"product":"workplace_search","action":"viewed","metric":"page"}', + body: '{"product":"workplace_search","action":"error","metric":"not_found"}', }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx index 715d61b31512c2..4df1428221de61 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx @@ -35,9 +35,21 @@ export const sendTelemetry = async ({ http, product, action, metric }: ISendTele /** * React component helpers - useful for on-page-load/views - * TODO: SendEnterpriseSearchTelemetry */ +export const SendEnterpriseSearchTelemetry: React.FC = ({ + action, + metric, +}) => { + const { http } = useContext(KibanaContext) as IKibanaContext; + + useEffect(() => { + sendTelemetry({ http, action, metric, product: 'enterprise_search' }); + }, [action, metric, http]); + + return null; +}; + export const SendAppSearchTelemetry: React.FC = ({ action, metric }) => { const { http } = useContext(KibanaContext) as IKibanaContext; diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index 83598a0dc971dc..b735db7c49520c 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -12,7 +12,6 @@ import { AppMountParameters, HttpSetup, } from 'src/core/public'; -import { i18n } from '@kbn/i18n'; import { FeatureCatalogueCategory, HomePublicPluginSetup, @@ -52,6 +51,25 @@ export class EnterpriseSearchPlugin implements Plugin { } public setup(core: CoreSetup, plugins: PluginsSetup) { + core.application.register({ + id: ENTERPRISE_SEARCH_PLUGIN.ID, + title: ENTERPRISE_SEARCH_PLUGIN.NAV_TITLE, + appRoute: ENTERPRISE_SEARCH_PLUGIN.URL, + category: DEFAULT_APP_CATEGORIES.enterpriseSearch, + mount: async (params: AppMountParameters) => { + const [coreStart] = await core.getStartServices(); + const { chrome } = coreStart; + chrome.docTitle.change(ENTERPRISE_SEARCH_PLUGIN.NAME); + + await this.getInitialData(coreStart.http); + + const { renderApp } = await import('./applications'); + const { EnterpriseSearch } = await import('./applications/enterprise_search'); + + return renderApp(EnterpriseSearch, params, coreStart, plugins, this.config, this.data); + }, + }); + core.application.register({ id: APP_SEARCH_PLUGIN.ID, title: APP_SEARCH_PLUGIN.NAME, @@ -94,22 +112,10 @@ export class EnterpriseSearchPlugin implements Plugin { plugins.home.featureCatalogue.registerSolution({ id: ENTERPRISE_SEARCH_PLUGIN.ID, title: ENTERPRISE_SEARCH_PLUGIN.NAME, - subtitle: i18n.translate('xpack.enterpriseSearch.featureCatalogue.subtitle', { - defaultMessage: 'Search everything', - }), + subtitle: ENTERPRISE_SEARCH_PLUGIN.SUBTITLE, icon: 'logoEnterpriseSearch', - descriptions: [ - i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription1', { - defaultMessage: 'Build a powerful search experience.', - }), - i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription2', { - defaultMessage: 'Connect your users to relevant data.', - }), - i18n.translate('xpack.enterpriseSearch.featureCatalogueDescription3', { - defaultMessage: 'Unify your team content.', - }), - ], - path: APP_SEARCH_PLUGIN.URL, // TODO: Change this to enterprise search overview page once available + descriptions: ENTERPRISE_SEARCH_PLUGIN.DESCRIPTIONS, + path: ENTERPRISE_SEARCH_PLUGIN.URL, }); plugins.home.featureCatalogue.register({ diff --git a/x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.test.ts b/x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.test.ts new file mode 100644 index 00000000000000..c3e2aff6551c94 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mockLogger } from '../../__mocks__'; + +import { registerTelemetryUsageCollector } from './telemetry'; + +describe('Enterprise Search Telemetry Usage Collector', () => { + const makeUsageCollectorStub = jest.fn(); + const registerStub = jest.fn(); + const usageCollectionMock = { + makeUsageCollector: makeUsageCollectorStub, + registerCollector: registerStub, + } as any; + + const savedObjectsRepoStub = { + get: () => ({ + attributes: { + 'ui_viewed.overview': 10, + 'ui_clicked.app_search': 2, + 'ui_clicked.workplace_search': 3, + }, + }), + incrementCounter: jest.fn(), + }; + const savedObjectsMock = { + createInternalRepository: jest.fn(() => savedObjectsRepoStub), + } as any; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('registerTelemetryUsageCollector', () => { + it('should make and register the usage collector', () => { + registerTelemetryUsageCollector(usageCollectionMock, savedObjectsMock, mockLogger); + + expect(registerStub).toHaveBeenCalledTimes(1); + expect(makeUsageCollectorStub).toHaveBeenCalledTimes(1); + expect(makeUsageCollectorStub.mock.calls[0][0].type).toBe('enterprise_search'); + expect(makeUsageCollectorStub.mock.calls[0][0].isReady()).toBe(true); + }); + }); + + describe('fetchTelemetryMetrics', () => { + it('should return existing saved objects data', async () => { + registerTelemetryUsageCollector(usageCollectionMock, savedObjectsMock, mockLogger); + const savedObjectsCounts = await makeUsageCollectorStub.mock.calls[0][0].fetch(); + + expect(savedObjectsCounts).toEqual({ + ui_viewed: { + overview: 10, + }, + ui_clicked: { + app_search: 2, + workplace_search: 3, + }, + }); + }); + + it('should return a default telemetry object if no saved data exists', async () => { + const emptySavedObjectsMock = { + createInternalRepository: () => ({ + get: () => ({ attributes: null }), + }), + } as any; + + registerTelemetryUsageCollector(usageCollectionMock, emptySavedObjectsMock, mockLogger); + const savedObjectsCounts = await makeUsageCollectorStub.mock.calls[0][0].fetch(); + + expect(savedObjectsCounts).toEqual({ + ui_viewed: { + overview: 0, + }, + ui_clicked: { + app_search: 0, + workplace_search: 0, + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.ts b/x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.ts new file mode 100644 index 00000000000000..a124a185b9a349 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/collectors/enterprise_search/telemetry.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { get } from 'lodash'; +import { SavedObjectsServiceStart, Logger } from 'src/core/server'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; + +import { getSavedObjectAttributesFromRepo } from '../lib/telemetry'; + +interface ITelemetry { + ui_viewed: { + overview: number; + }; + ui_clicked: { + app_search: number; + workplace_search: number; + }; +} + +export const ES_TELEMETRY_NAME = 'enterprise_search_telemetry'; + +/** + * Register the telemetry collector + */ + +export const registerTelemetryUsageCollector = ( + usageCollection: UsageCollectionSetup, + savedObjects: SavedObjectsServiceStart, + log: Logger +) => { + const telemetryUsageCollector = usageCollection.makeUsageCollector({ + type: 'enterprise_search', + fetch: async () => fetchTelemetryMetrics(savedObjects, log), + isReady: () => true, + schema: { + ui_viewed: { + overview: { type: 'long' }, + }, + ui_clicked: { + app_search: { type: 'long' }, + workplace_search: { type: 'long' }, + }, + }, + }); + usageCollection.registerCollector(telemetryUsageCollector); +}; + +/** + * Fetch the aggregated telemetry metrics from our saved objects + */ + +const fetchTelemetryMetrics = async (savedObjects: SavedObjectsServiceStart, log: Logger) => { + const savedObjectsRepository = savedObjects.createInternalRepository(); + const savedObjectAttributes = await getSavedObjectAttributesFromRepo( + ES_TELEMETRY_NAME, + savedObjectsRepository, + log + ); + + const defaultTelemetrySavedObject: ITelemetry = { + ui_viewed: { + overview: 0, + }, + ui_clicked: { + app_search: 0, + workplace_search: 0, + }, + }; + + // If we don't have an existing/saved telemetry object, return the default + if (!savedObjectAttributes) { + return defaultTelemetrySavedObject; + } + + return { + ui_viewed: { + overview: get(savedObjectAttributes, 'ui_viewed.overview', 0), + }, + ui_clicked: { + app_search: get(savedObjectAttributes, 'ui_clicked.app_search', 0), + workplace_search: get(savedObjectAttributes, 'ui_clicked.workplace_search', 0), + }, + } as ITelemetry; +}; diff --git a/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.test.ts b/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.test.ts index aae162c23ccb42..6cf0be9fd1f313 100644 --- a/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.test.ts +++ b/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.test.ts @@ -15,7 +15,7 @@ import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server'; import { getSavedObjectAttributesFromRepo, incrementUICounter } from './telemetry'; -describe('App Search Telemetry Usage Collector', () => { +describe('Telemetry helpers', () => { beforeEach(() => { jest.clearAllMocks(); }); diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 617210a544262a..729a03d24065e2 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -31,8 +31,10 @@ import { IEnterpriseSearchRequestHandler, } from './lib/enterprise_search_request_handler'; -import { registerConfigDataRoute } from './routes/enterprise_search/config_data'; +import { enterpriseSearchTelemetryType } from './saved_objects/enterprise_search/telemetry'; +import { registerTelemetryUsageCollector as registerESTelemetryUsageCollector } from './collectors/enterprise_search/telemetry'; import { registerTelemetryRoute } from './routes/enterprise_search/telemetry'; +import { registerConfigDataRoute } from './routes/enterprise_search/config_data'; import { appSearchTelemetryType } from './saved_objects/app_search/telemetry'; import { registerTelemetryUsageCollector as registerASTelemetryUsageCollector } from './collectors/app_search/telemetry'; @@ -81,8 +83,12 @@ export class EnterpriseSearchPlugin implements Plugin { name: ENTERPRISE_SEARCH_PLUGIN.NAME, order: 0, icon: 'logoEnterpriseSearch', - navLinkId: APP_SEARCH_PLUGIN.ID, // TODO - remove this once functional tests no longer rely on navLinkId - app: ['kibana', APP_SEARCH_PLUGIN.ID, WORKPLACE_SEARCH_PLUGIN.ID], + app: [ + 'kibana', + ENTERPRISE_SEARCH_PLUGIN.ID, + APP_SEARCH_PLUGIN.ID, + WORKPLACE_SEARCH_PLUGIN.ID, + ], catalogue: [ENTERPRISE_SEARCH_PLUGIN.ID, APP_SEARCH_PLUGIN.ID, WORKPLACE_SEARCH_PLUGIN.ID], privileges: null, }); @@ -94,14 +100,16 @@ export class EnterpriseSearchPlugin implements Plugin { const dependencies = { config, security, request, log }; const { hasAppSearchAccess, hasWorkplaceSearchAccess } = await checkAccess(dependencies); + const showEnterpriseSearchOverview = hasAppSearchAccess || hasWorkplaceSearchAccess; return { navLinks: { + enterpriseSearch: showEnterpriseSearchOverview, appSearch: hasAppSearchAccess, workplaceSearch: hasWorkplaceSearchAccess, }, catalogue: { - enterpriseSearch: hasAppSearchAccess || hasWorkplaceSearchAccess, + enterpriseSearch: showEnterpriseSearchOverview, appSearch: hasAppSearchAccess, workplaceSearch: hasWorkplaceSearchAccess, }, @@ -123,6 +131,7 @@ export class EnterpriseSearchPlugin implements Plugin { /** * Bootstrap the routes, saved objects, and collector for telemetry */ + savedObjects.registerType(enterpriseSearchTelemetryType); savedObjects.registerType(appSearchTelemetryType); savedObjects.registerType(workplaceSearchTelemetryType); let savedObjectsStarted: SavedObjectsServiceStart; @@ -131,6 +140,7 @@ export class EnterpriseSearchPlugin implements Plugin { savedObjectsStarted = coreStart.savedObjects; if (usageCollection) { + registerESTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); registerASTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); registerWSTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); } diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts index 7ed1d7b17753c3..bfc07c8b64ef50 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts @@ -9,12 +9,13 @@ import { schema } from '@kbn/config-schema'; import { IRouteDependencies } from '../../plugin'; import { incrementUICounter } from '../../collectors/lib/telemetry'; +import { ES_TELEMETRY_NAME } from '../../collectors/enterprise_search/telemetry'; import { AS_TELEMETRY_NAME } from '../../collectors/app_search/telemetry'; import { WS_TELEMETRY_NAME } from '../../collectors/workplace_search/telemetry'; const productToTelemetryMap = { + enterprise_search: ES_TELEMETRY_NAME, app_search: AS_TELEMETRY_NAME, workplace_search: WS_TELEMETRY_NAME, - enterprise_search: 'TODO', }; export function registerTelemetryRoute({ diff --git a/x-pack/plugins/enterprise_search/server/saved_objects/enterprise_search/telemetry.ts b/x-pack/plugins/enterprise_search/server/saved_objects/enterprise_search/telemetry.ts new file mode 100644 index 00000000000000..54044e67939da5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/saved_objects/enterprise_search/telemetry.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* istanbul ignore file */ + +import { SavedObjectsType } from 'src/core/server'; +import { ES_TELEMETRY_NAME } from '../../collectors/enterprise_search/telemetry'; + +export const enterpriseSearchTelemetryType: SavedObjectsType = { + name: ES_TELEMETRY_NAME, + hidden: false, + namespaceType: 'agnostic', + mappings: { + dynamic: false, + properties: {}, + }, +}; diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 2435d8a9aaf04c..a7330d3ebd5529 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -51,6 +51,27 @@ } } }, + "enterprise_search": { + "properties": { + "ui_viewed": { + "properties": { + "overview": { + "type": "long" + } + } + }, + "ui_clicked": { + "properties": { + "app_search": { + "type": "long" + }, + "workplace_search": { + "type": "long" + } + } + } + } + }, "workplace_search": { "properties": { "ui_viewed": { diff --git a/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts b/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts index d7a0dfa1cf80a2..091bbccd6f87a8 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts @@ -49,7 +49,13 @@ export default function navLinksTests({ getService }: FtrProviderContext) { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('navLinks'); expect(uiCapabilities.value!.navLinks).to.eql( - navLinksBuilder.except('ml', 'monitoring', 'appSearch', 'workplaceSearch') + navLinksBuilder.except( + 'ml', + 'monitoring', + 'enterpriseSearch', + 'appSearch', + 'workplaceSearch' + ) ); break; case 'foo_all':