diff --git a/x-pack/plugins/enterprise_search/kibana.json b/x-pack/plugins/enterprise_search/kibana.json
index d0c4c9733da2a..3121d6bd470b0 100644
--- a/x-pack/plugins/enterprise_search/kibana.json
+++ b/x-pack/plugins/enterprise_search/kibana.json
@@ -2,7 +2,7 @@
"id": "enterpriseSearch",
"version": "1.0.0",
"kibanaVersion": "kibana",
- "requiredPlugins": ["home"],
+ "requiredPlugins": ["home", "licensing"],
"configPath": ["enterpriseSearch"],
"optionalPlugins": ["usageCollection"],
"server": true,
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts
index cfe5a1e4c4ee2..5b19055115fde 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts
@@ -6,6 +6,7 @@
export { mockHistory } from './react_router_history.mock';
export { mockKibanaContext } from './kibana_context.mock';
+export { mockLicenseContext } from './license_context.mock';
export { mountWithKibanaContext } from './mount_with_context.mock';
// Note: shallow_usecontext must be imported directly as a file
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts
index fcfa1b0a21f13..3363c031da8ae 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts
@@ -14,4 +14,5 @@ export const mockKibanaContext = {
http: httpServiceMock.createSetupContract(),
setBreadcrumbs: jest.fn(),
enterpriseSearchUrl: 'http://localhost:3002',
+ license$: jest.fn(),
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts
new file mode 100644
index 0000000000000..7c37ecc7cde1b
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 { licensingMock } from '../../../../licensing/public/mocks';
+
+export const mockLicenseContext = {
+ license: licensingMock.createLicense(),
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts
index 5193a0cd299f8..20add45e16b58 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts
@@ -5,14 +5,15 @@
*/
/**
- * NOTE: This variable name MUST start with 'mock*' in order for
+ * NOTE: These variable names MUST start with 'mock*' in order for
* Jest to accept its use within a jest.mock()
*/
import { mockKibanaContext } from './kibana_context.mock';
+import { mockLicenseContext } from './license_context.mock';
jest.mock('react', () => ({
...jest.requireActual('react'),
- useContext: jest.fn(() => mockKibanaContext),
+ useContext: jest.fn(() => ({ ...mockKibanaContext, ...mockLicenseContext })),
}));
/**
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx
index 2b712721a7fa5..5d029d6c4ba8a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx
@@ -11,6 +11,7 @@ import { act } from 'react-dom/test-utils';
import { render } from 'enzyme';
import { KibanaContext } from '../../../';
+import { LicenseContext } from '../../../shared/licensing';
import { mountWithKibanaContext, mockKibanaContext } from '../../../__mocks__';
import { EmptyState, ErrorState, NoUserState } from '../empty_states';
@@ -24,7 +25,9 @@ describe('EngineOverview', () => {
// We use render() instead of mount() here to not trigger lifecycle methods (i.e., useEffect)
const wrapper = render(
-
+
+
+
);
@@ -85,7 +88,7 @@ describe('EngineOverview', () => {
});
it('renders', () => {
- expect(wrapper.find(EngineTable)).toHaveLength(2);
+ expect(wrapper.find(EngineTable)).toHaveLength(1);
});
it('calls the engines API', () => {
@@ -95,12 +98,6 @@ describe('EngineOverview', () => {
pageIndex: 1,
},
});
- expect(mockApi).toHaveBeenNthCalledWith(2, '/api/app_search/engines', {
- query: {
- type: 'meta',
- pageIndex: 1,
- },
- });
});
describe('pagination', () => {
@@ -130,13 +127,36 @@ describe('EngineOverview', () => {
expect(getTablePagination().pageIndex).toEqual(4);
});
});
+
+ describe('when on a platinum license', () => {
+ beforeAll(async () => {
+ mockApi.mockClear();
+ wrapper = await mountWithApiMock({
+ license: { type: 'platinum', isActive: true },
+ get: mockApi,
+ });
+ });
+
+ it('renders a 2nd meta engines table', () => {
+ expect(wrapper.find(EngineTable)).toHaveLength(2);
+ });
+
+ it('makes a 2nd call to the engines API with type meta', () => {
+ expect(mockApi).toHaveBeenNthCalledWith(2, '/api/app_search/engines', {
+ query: {
+ type: 'meta',
+ pageIndex: 1,
+ },
+ });
+ });
+ });
});
/**
* Test helpers
*/
- const mountWithApiMock = async ({ get }) => {
+ const mountWithApiMock = async ({ get, license }) => {
let wrapper;
const httpMock = { ...mockKibanaContext.http, get };
@@ -144,7 +164,12 @@ describe('EngineOverview', () => {
// TBH, I don't fully understand why since Enzyme's mount is supposed to
// have act() baked in - could be because of the wrapping context provider?
await act(async () => {
- wrapper = mountWithKibanaContext(, { http: httpMock });
+ wrapper = mountWithKibanaContext(
+
+
+ ,
+ { http: httpMock }
+ );
});
wrapper.update(); // This seems to be required for the DOM to actually update
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx
index d87c36cd9b9d6..1e1a583b5bcdb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx
@@ -17,6 +17,7 @@ import {
import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs';
import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
+import { LicenseContext, ILicenseContext, hasPlatinumLicense } from '../../../shared/licensing';
import { KibanaContext, IKibanaContext } from '../../../index';
import EnginesIcon from '../../assets/engine.svg';
@@ -30,6 +31,7 @@ import './engine_overview.scss';
export const EngineOverview: ReactFC<> = () => {
const { http } = useContext(KibanaContext) as IKibanaContext;
+ const { license } = useContext(LicenseContext) as ILicenseContext;
const [isLoading, setIsLoading] = useState(true);
const [hasNoAccount, setHasNoAccount] = useState(false);
@@ -72,11 +74,13 @@ export const EngineOverview: ReactFC<> = () => {
}, [enginesPage]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
- const params = { type: 'meta', pageIndex: metaEnginesPage };
- const callbacks = { setResults: setMetaEngines, setResultsTotal: setMetaEnginesTotal };
+ if (hasPlatinumLicense(license)) {
+ const params = { type: 'meta', pageIndex: metaEnginesPage };
+ const callbacks = { setResults: setMetaEngines, setResultsTotal: setMetaEnginesTotal };
- setEnginesData(params, callbacks);
- }, [metaEnginesPage]); // eslint-disable-line react-hooks/exhaustive-deps
+ setEnginesData(params, callbacks);
+ }
+ }, [license, metaEnginesPage]); // eslint-disable-line react-hooks/exhaustive-deps
if (hasErrorConnecting) return ;
if (hasNoAccount) return ;
diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.ts b/x-pack/plugins/enterprise_search/public/applications/index.test.ts
index 7ece7e153c154..7ea5b97feac6c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/index.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/index.test.ts
@@ -5,14 +5,20 @@
*/
import { coreMock } from 'src/core/public/mocks';
-import { renderApp } from '../applications';
+import { licensingMock } from '../../../licensing/public/mocks';
+
+import { renderApp } from './';
describe('renderApp', () => {
it('mounts and unmounts UI', () => {
const params = coreMock.createAppMountParamters();
const core = coreMock.createStart();
+ const config = {};
+ const plugins = {
+ licensing: licensingMock.createSetup(),
+ };
- const unmount = renderApp(core, params, {});
+ const unmount = renderApp(core, params, config, plugins);
expect(params.element.querySelector('.setup-guide')).not.toBeNull();
unmount();
expect(params.element.innerHTML).toEqual('');
diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx
index 473d395c1e604..0fb18a8b627bb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/index.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx
@@ -9,8 +9,10 @@ import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Redirect } from 'react-router-dom';
import { CoreStart, AppMountParams, HttpHandler } from 'src/core/public';
-import { ClientConfigType } from '../plugin';
+import { ClientConfigType, PluginsSetup } from '../plugin';
import { TSetBreadcrumbs } from './shared/kibana_breadcrumbs';
+import { ILicense } from '../../../../licensing/public';
+import { LicenseProvider } from './shared/licensing';
import { AppSearch } from './app_search';
@@ -18,29 +20,38 @@ export interface IKibanaContext {
enterpriseSearchUrl?: string;
http(): HttpHandler;
setBreadCrumbs(): TSetBreadcrumbs;
+ license$: Observable;
}
export const KibanaContext = React.createContext();
-export const renderApp = (core: CoreStart, params: AppMountParams, config: ClientConfigType) => {
+export const renderApp = (
+ core: CoreStart,
+ params: AppMountParams,
+ config: ClientConfigType,
+ plugins: PluginsSetup
+) => {
ReactDOM.render(
-
-
- {/* This will eventually contain an Enterprise Search landing page,
- and we'll also actually have a /workplace_search route */}
-
-
-
-
-
-
+
+
+
+ {/* This will eventually contain an Enterprise Search landing page,
+ and we'll also actually have a /workplace_search route */}
+
+
+
+
+
+
+
,
params.element
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts
new file mode 100644
index 0000000000000..9c8c1417d48db
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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 { LicenseContext, LicenseProvider, ILicenseContext } from './license_context';
+export { hasPlatinumLicense } from './license_checks';
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts
new file mode 100644
index 0000000000000..e21bf004b39a2
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts
@@ -0,0 +1,33 @@
+/*
+ * 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 { hasPlatinumLicense } from './license_checks';
+
+describe('hasPlatinumLicense', () => {
+ it('is true for platinum licenses', () => {
+ expect(hasPlatinumLicense({ isActive: true, type: 'platinum' })).toEqual(true);
+ });
+
+ it('is true for enterprise licenses', () => {
+ expect(hasPlatinumLicense({ isActive: true, type: 'enterprise' })).toEqual(true);
+ });
+
+ it('is true for trial licenses', () => {
+ expect(hasPlatinumLicense({ isActive: true, type: 'platinum' })).toEqual(true);
+ });
+
+ it('is false if the current license is expired', () => {
+ expect(hasPlatinumLicense({ isActive: false, type: 'platinum' })).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: false, type: 'enterprise' })).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: false, type: 'trial' })).toEqual(false);
+ });
+
+ it('is false for licenses below platinum', () => {
+ expect(hasPlatinumLicense({ isActive: true, type: 'basic' })).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: false, type: 'standard' })).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: true, type: 'gold' })).toEqual(false);
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts
new file mode 100644
index 0000000000000..7d0de8a093b31
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 { ILicense } from '../../../../../../licensing/public';
+
+export const hasPlatinumLicense = (license: ILicenseContext) => {
+ return license?.isActive && ['platinum', 'enterprise', 'trial'].includes(license?.type);
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx
new file mode 100644
index 0000000000000..3385f79d3d075
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx
@@ -0,0 +1,28 @@
+/*
+ * 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 { mountWithKibanaContext } from '../../__mocks__';
+import { LicenseContext, ILicenseContext } from './';
+
+describe('LicenseProvider', () => {
+ const MockComponent: React.FC<> = () => {
+ const { license } = useContext(LicenseContext) as ILicenseContext;
+ return {license.type}
;
+ };
+
+ it('renders children', () => {
+ const wrapper = mountWithKibanaContext(
+
+
+
+ );
+
+ expect(wrapper.find('.license-test')).toHaveLength(1);
+ expect(wrapper.text()).toEqual('basic');
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx
new file mode 100644
index 0000000000000..c8295196aedb5
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx
@@ -0,0 +1,27 @@
+/*
+ * 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 useObservable from 'react-use/lib/useObservable';
+
+import { KibanaContext, IKibanaContext } from '../../';
+
+import { ILicense } from '../../../../licensing/public';
+
+export interface ILicenseContext {
+ license?: ILicense;
+}
+
+export const LicenseContext = React.createContext();
+
+export const LicenseProvider: React.FC<> = ({ children }) => {
+ // Listen for changes to license subscription
+ const { license$ } = useContext(KibanaContext) as IKibanaContext;
+ const license = useObservable(license$);
+
+ // Render rest of application and pass down license via context
+ return ;
+};
diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts
index 5331eb1e8f51f..cf495b6a6f9de 100644
--- a/x-pack/plugins/enterprise_search/public/plugin.ts
+++ b/x-pack/plugins/enterprise_search/public/plugin.ts
@@ -17,6 +17,7 @@ import {
HomePublicPluginSetup,
} from '../../../../src/plugins/home/public';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
+import { LicensingPluginSetup } from '../../licensing/public';
import AppSearchLogo from './applications/app_search/assets/logo.svg';
@@ -24,7 +25,8 @@ export interface ClientConfigType {
host?: string;
}
export interface PluginsSetup {
- home?: HomePublicPluginSetup;
+ home: HomePublicPluginSetup;
+ licensing: LicensingPluginSetup;
}
export class EnterpriseSearchPlugin implements Plugin {
@@ -43,11 +45,12 @@ export class EnterpriseSearchPlugin implements Plugin {
euiIconType: AppSearchLogo, // TODO: Temporary - App Search will likely no longer need an icon once the nav structure changes.
category: DEFAULT_APP_CATEGORIES.management, // TODO - This is likely not final/correct
order: 10, // TODO - This will also likely not be needed once new nav structure changes land
- async mount(params: AppMountParameters) {
+ mount: async (params: AppMountParameters) => {
const [coreStart] = await core.getStartServices();
+
const { renderApp } = await import('./applications');
- return renderApp(coreStart, params, config);
+ return renderApp(coreStart, params, config, plugins);
},
});