[serverless] Create Search Serverless plugin (#156037)

This commit is contained in:
Clint Andrew Hall 2023-04-27 21:49:59 -04:00 committed by GitHub
parent 604a02f6a1
commit 965b327ca6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 451 additions and 139 deletions

1
.github/CODEOWNERS vendored
View file

@ -575,6 +575,7 @@ packages/kbn-server-http-tools @elastic/kibana-core
packages/kbn-server-route-repository @elastic/apm-ui
x-pack/plugins/serverless @elastic/appex-sharedux
packages/serverless/project_switcher @elastic/appex-sharedux
x-pack/plugins/serverless_search @elastic/appex-sharedux
packages/serverless/storybook/config @elastic/appex-sharedux
packages/serverless/types @elastic/appex-sharedux
test/plugin_functional/plugins/session_notifications @elastic/kibana-core

View file

@ -1,2 +1,15 @@
# Search Project Config
## Disable APM and Uptime, enable Enterprise Search
xpack.apm.enabled: false
xpack.uptime.enabled: false
enterpriseSearch.enabled: true
## Enable the Serverless Search plugin
xpack.serverless.search.enabled: true
## Set the home route
uiSettings.overrides.defaultRoute: /app/enterprise_search/content/search_indices
## Set the dev project switcher current type
xpack.serverless.plugin.developer.projectSwitcher.currentType: 'search'

View file

@ -714,6 +714,10 @@ Kibana.
|
|{kib-repo}blob/{branch}/x-pack/plugins/serverless_search/README.mdx[serverlessSearch]
|This plugin contains configuration and code used to create a Serverless Search project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana.
|{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView]
|Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time.

View file

@ -576,6 +576,7 @@
"@kbn/server-route-repository": "link:packages/kbn-server-route-repository",
"@kbn/serverless": "link:x-pack/plugins/serverless",
"@kbn/serverless-project-switcher": "link:packages/serverless/project_switcher",
"@kbn/serverless-search": "link:x-pack/plugins/serverless_search",
"@kbn/serverless-types": "link:packages/serverless/types",
"@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications",
"@kbn/session-view-plugin": "link:x-pack/plugins/session_view",

View file

@ -116,6 +116,7 @@ pageLoadAssetSize:
security: 65433
securitySolution: 66738
serverless: 16573
serverlessSearch: 17548
sessionView: 77750
share: 71239
snapshotRestore: 79032

View file

@ -1144,6 +1144,8 @@
"@kbn/serverless/*": ["x-pack/plugins/serverless/*"],
"@kbn/serverless-project-switcher": ["packages/serverless/project_switcher"],
"@kbn/serverless-project-switcher/*": ["packages/serverless/project_switcher/*"],
"@kbn/serverless-search": ["x-pack/plugins/serverless_search"],
"@kbn/serverless-search/*": ["x-pack/plugins/serverless_search/*"],
"@kbn/serverless-storybook-config": ["packages/serverless/storybook/config"],
"@kbn/serverless-storybook-config/*": ["packages/serverless/storybook/config/*"],
"@kbn/serverless-types": ["packages/serverless/types"],

View file

@ -36,8 +36,8 @@ export const mockAllActions = {
*/
jest.mock('kea', () => ({
...(jest.requireActual('kea') as object),
useValues: jest.fn(() => ({ ...mockAllValues })),
useActions: jest.fn(() => ({ ...mockAllActions })),
useValues: jest.fn(() => ({ ...mockAllValues })),
}));
/**

View file

@ -35,6 +35,7 @@ export const mockKibanaValues = {
guidedOnboarding: {},
history: mockHistory,
isCloud: false,
isSidebarEnabled: true,
lens: {
EmbeddableComponent: jest.fn(),
stateHelperApi: jest.fn().mockResolvedValue({

View file

@ -27,8 +27,8 @@ import { renderApp, renderHeaderActions } from '.';
describe('renderApp', () => {
const kibanaDeps = {
params: coreMock.createAppMountParameters(),
core: coreMock.createStart(),
params: coreMock.createAppMountParameters(),
plugins: {
charts: chartPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),

View file

@ -37,10 +37,26 @@ import { mountLicensingLogic } from './shared/licensing';
export const renderApp = (
App: React.FC<InitialAppData>,
{ params, core, plugins }: { params: AppMountParameters; core: CoreStart; plugins: PluginsStart },
{
params,
core,
plugins,
isSidebarEnabled = true,
}: {
core: CoreStart;
isSidebarEnabled: boolean;
params: AppMountParameters;
plugins: PluginsStart;
},
{ config, data }: { config: ClientConfigType; data: ClientData }
) => {
const { publicUrl, errorConnectingMessage, ...initialData } = data;
const { access, features, publicUrl, errorConnectingMessage, readOnlyMode, ...initialData } =
data;
const { history } = params;
const { application, chrome, http, uiSettings } = core;
const { capabilities, navigateToUrl } = application;
const { charts, cloud, guidedOnboarding, lens, security } = plugins;
const entCloudHost = getCloudEnterpriseSearchHost(plugins.cloud);
externalUrl.enterpriseSearchUrl = publicUrl || entCloudHost || config.host || '';
@ -48,44 +64,46 @@ export const renderApp = (
hasAppSearchAccess: false,
hasWorkplaceSearchAccess: false,
};
const productAccess = data.access || noProductAccess;
const productFeatures = data.features ?? { ...DEFAULT_PRODUCT_FEATURES };
const productAccess = access || noProductAccess;
const productFeatures = features ?? { ...DEFAULT_PRODUCT_FEATURES };
const EmptyContext: FC = ({ children }) => <>{children}</>;
const CloudContext = plugins.cloud?.CloudContextProvider || EmptyContext;
const CloudContext = cloud?.CloudContextProvider || EmptyContext;
resetContext({ createStore: true });
const store = getContext().store;
const unmountKibanaLogic = mountKibanaLogic({
application: core.application,
capabilities: core.application.capabilities,
application,
capabilities,
charts,
cloud,
config,
data: plugins.data,
lens: plugins.lens,
guidedOnboarding,
history,
isSidebarEnabled,
lens,
navigateToUrl,
productAccess,
productFeatures,
charts: plugins.charts,
cloud: plugins.cloud,
uiSettings: core.uiSettings,
guidedOnboarding: plugins.guidedOnboarding,
history: params.history,
navigateToUrl: core.application.navigateToUrl,
security: plugins.security,
setBreadcrumbs: core.chrome.setBreadcrumbs,
setChromeIsVisible: core.chrome.setIsVisible,
setDocTitle: core.chrome.docTitle.change,
renderHeaderActions: (HeaderActions) =>
params.setHeaderActionMenu((el) => renderHeaderActions(HeaderActions, store, el)),
security,
setBreadcrumbs: chrome.setBreadcrumbs,
setChromeIsVisible: chrome.setIsVisible,
setDocTitle: chrome.docTitle.change,
uiSettings,
});
const unmountLicensingLogic = mountLicensingLogic({
license$: plugins.licensing.license$,
canManageLicense: core.application.capabilities.management?.stack?.license_management,
license$: plugins.licensing.license$,
});
const unmountHttpLogic = mountHttpLogic({
http: core.http,
errorConnectingMessage,
readOnlyMode: initialData.readOnlyMode,
http,
readOnlyMode,
});
const unmountFlashMessagesLogic = mountFlashMessagesLogic();

View file

@ -33,27 +33,26 @@ type RequiredFieldsOnly<T> = {
};
interface KibanaLogicProps {
application: ApplicationStart;
config: ClientConfigType;
productAccess: ProductAccess;
productFeatures: ProductFeatures;
// Kibana core
capabilities: Capabilities;
charts: ChartsPluginStart;
cloud?: CloudSetup;
config: ClientConfigType;
data: DataPublicPluginStart;
guidedOnboarding: GuidedOnboardingPluginStart;
history: ScopedHistory;
isSidebarEnabled: boolean;
lens: LensPublicStart;
navigateToUrl: RequiredFieldsOnly<ApplicationStart['navigateToUrl']>;
productAccess: ProductAccess;
productFeatures: ProductFeatures;
renderHeaderActions(HeaderActions: FC): void;
security: SecurityPluginStart;
setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void;
setChromeIsVisible(isVisible: boolean): void;
setDocTitle(title: string): void;
renderHeaderActions(HeaderActions: FC): void;
// Required plugins
charts: ChartsPluginStart;
guidedOnboarding: GuidedOnboardingPluginStart;
security: SecurityPluginStart;
uiSettings: IUiSettingsClient;
// Optional plugins
cloud?: CloudSetup;
}
export interface KibanaValues extends Omit<KibanaLogicProps, 'cloud'> {
cloud: Partial<CloudSetup>;
data: DataPublicPluginStart;
@ -73,6 +72,7 @@ export const KibanaLogic = kea<MakeLogicType<KibanaValues>>({
data: [props.data, {}],
guidedOnboarding: [props.guidedOnboarding, {}],
history: [props.history, {}],
isSidebarEnabled: [props.isSidebarEnabled, {}],
lens: [props.lens, {}],
navigateToUrl: [
(url: string, options?: CreateHrefOptions) => {

View file

@ -35,7 +35,11 @@ describe('useEnterpriseSearchContentNav', () => {
it('returns an array of top-level Enterprise Search nav items', () => {
const fullProductAccess: ProductAccess = DEFAULT_PRODUCT_ACCESS;
setMockValues({ productAccess: fullProductAccess, productFeatures: DEFAULT_PRODUCT_FEATURES });
setMockValues({
isSidebarEnabled: true,
productAccess: fullProductAccess,
productFeatures: DEFAULT_PRODUCT_FEATURES,
});
expect(useEnterpriseSearchNav()).toEqual([
{
@ -48,9 +52,9 @@ describe('useEnterpriseSearchContentNav', () => {
name: 'Elasticsearch',
},
{
href: '/app/enterprise_search/search_experiences',
id: 'searchExperiences',
name: 'Search Experiences',
href: '/app/enterprise_search/search_experiences',
},
],
name: 'Overview',
@ -74,12 +78,11 @@ describe('useEnterpriseSearchContentNav', () => {
},
{
id: 'applications',
name: 'Applications',
items: [
{
href: '/app/enterprise_search/content/engines',
id: 'searchApplications',
name: 'Search Applications',
href: '/app/enterprise_search/content/engines',
},
{
href: '/app/enterprise_search/analytics',
@ -87,6 +90,7 @@ describe('useEnterpriseSearchContentNav', () => {
name: 'Behavioral Analytics',
},
],
name: 'Applications',
},
{
id: 'standaloneExperiences',
@ -114,7 +118,11 @@ describe('useEnterpriseSearchContentNav', () => {
hasWorkplaceSearchAccess: false,
};
setMockValues({ productAccess: noProductAccess, productFeatures: DEFAULT_PRODUCT_FEATURES });
setMockValues({
isSidebarEnabled: true,
productAccess: noProductAccess,
productFeatures: DEFAULT_PRODUCT_FEATURES,
});
mockKibanaValues.uiSettings.get.mockReturnValue(false);
const esNav = useEnterpriseSearchNav();
@ -130,6 +138,7 @@ describe('useEnterpriseSearchContentNav', () => {
};
setMockValues({
isSidebarEnabled: true,
productAccess: workplaceSearchProductAccess,
productFeatures: DEFAULT_PRODUCT_FEATURES,
});
@ -157,6 +166,7 @@ describe('useEnterpriseSearchContentNav', () => {
};
setMockValues({
isSidebarEnabled: true,
productAccess: appSearchProductAccess,
productFeatures: DEFAULT_PRODUCT_FEATURES,
});
@ -183,6 +193,7 @@ describe('useEnterpriseSearchEngineNav', () => {
jest.clearAllMocks();
mockKibanaValues.uiSettings.get.mockReturnValue(true);
setMockValues({
isSidebarEnabled: true,
productAccess: DEFAULT_PRODUCT_ACCESS,
productFeatures: DEFAULT_PRODUCT_FEATURES,
});
@ -200,9 +211,9 @@ describe('useEnterpriseSearchEngineNav', () => {
name: 'Elasticsearch',
},
{
href: '/app/enterprise_search/search_experiences',
id: 'searchExperiences',
name: 'Search Experiences',
href: '/app/enterprise_search/search_experiences',
},
],
name: 'Overview',
@ -225,12 +236,11 @@ describe('useEnterpriseSearchEngineNav', () => {
},
{
id: 'applications',
name: 'Applications',
items: [
{
href: '/app/enterprise_search/content/engines',
id: 'searchApplications',
name: 'Search Applications',
href: '/app/enterprise_search/content/engines',
},
{
href: '/app/enterprise_search/analytics',
@ -238,6 +248,7 @@ describe('useEnterpriseSearchEngineNav', () => {
name: 'Behavioral Analytics',
},
],
name: 'Applications',
},
{
id: 'standaloneExperiences',
@ -346,9 +357,9 @@ describe('useEnterpriseSearchAnalyticsNav', () => {
name: 'Elasticsearch',
},
{
href: '/app/enterprise_search/search_experiences',
id: 'searchExperiences',
name: 'Search Experiences',
href: '/app/enterprise_search/search_experiences',
},
],
name: 'Overview',
@ -366,7 +377,6 @@ describe('useEnterpriseSearchAnalyticsNav', () => {
},
{
id: 'applications',
name: 'Applications',
items: [
{
href: '/app/enterprise_search/content/engines',
@ -379,6 +389,7 @@ describe('useEnterpriseSearchAnalyticsNav', () => {
name: 'Behavioral Analytics',
},
],
name: 'Applications',
},
{
id: 'standaloneExperiences',
@ -400,7 +411,9 @@ describe('useEnterpriseSearchAnalyticsNav', () => {
beforeEach(() => {
jest.clearAllMocks();
setMockValues({});
setMockValues({
isSidebarEnabled: true,
});
});
it('returns basic nav all params are empty', () => {

View file

@ -30,7 +30,8 @@ import { KibanaLogic } from '../kibana';
import { generateNavLink } from './nav_link_helpers';
export const useEnterpriseSearchNav = () => {
const { productAccess, productFeatures } = useValues(KibanaLogic);
const { isSidebarEnabled, productAccess, productFeatures } = useValues(KibanaLogic);
if (!isSidebarEnabled) return undefined;
const navItems: Array<EuiSideNavItemType<unknown>> = [
{
@ -174,6 +175,7 @@ export const useEnterpriseSearchNav = () => {
export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?: boolean) => {
const navItems = useEnterpriseSearchNav();
if (!navItems) return undefined;
if (!engineName) return navItems;
const applicationsItem = navItems.find((item) => item.id === 'applications');
if (!applicationsItem || !applicationsItem.items) return navItems;
@ -255,6 +257,9 @@ export const useEnterpriseSearchAnalyticsNav = (
}
) => {
const navItems = useEnterpriseSearchNav();
if (!navItems) return undefined;
const applicationsNav = navItems.find((item) => item.id === 'applications');
const analyticsNav = applicationsNav?.items?.find((item) => item.id === 'analyticsCollections');

View file

@ -35,13 +35,13 @@ import './page_template.scss';
export type PageTemplateProps = KibanaPageTemplateProps & {
customPageSections?: boolean; // If false, automatically wraps children in an EuiPageSection
emptyState?: React.ReactNode;
hideFlashMessages?: boolean;
isLoading?: boolean;
emptyState?: React.ReactNode;
setPageChrome?: React.ReactNode;
// Used by product-specific page templates
pageChrome?: BreadcrumbTrail;
pageViewTelemetry?: string;
setPageChrome?: React.ReactNode;
};
export const EnterpriseSearchPageTemplateWrapper: React.FC<PageTemplateProps> = ({
@ -73,7 +73,11 @@ export const EnterpriseSearchPageTemplateWrapper: React.FC<PageTemplateProps> =
),
}}
isEmptyState={isEmptyState && !isLoading}
solutionNav={solutionNav ? { icon: 'logoEnterpriseSearch', ...solutionNav } : undefined}
solutionNav={
solutionNav && solutionNav.items
? { icon: 'logoEnterpriseSearch', ...solutionNav }
: undefined
}
>
{setPageChrome}
{readOnlyMode && (

View file

@ -12,3 +12,5 @@ import { EnterpriseSearchPlugin } from './plugin';
export const plugin = (initializerContext: PluginInitializerContext) => {
return new EnterpriseSearchPlugin(initializerContext);
};
export type { EnterpriseSearchPublicSetup, EnterpriseSearchPublicStart } from './plugin';

View file

@ -38,10 +38,13 @@ import { ClientConfigType, InitialAppData } from '../common/types';
import { docLinks } from './applications/shared/doc_links';
export interface ClientData extends InitialAppData {
publicUrl?: string;
errorConnectingMessage?: string;
publicUrl?: string;
}
export type EnterpriseSearchPublicSetup = ReturnType<EnterpriseSearchPlugin['setup']>;
export type EnterpriseSearchPublicStart = ReturnType<EnterpriseSearchPlugin['start']>;
interface PluginsSetup {
cloud?: CloudSetup;
home?: HomePublicPluginSetup;
@ -49,8 +52,8 @@ interface PluginsSetup {
}
export interface PluginsStart {
cloud?: CloudSetup & CloudStart;
charts: ChartsPluginStart;
cloud?: CloudSetup & CloudStart;
data: DataPublicPluginStart;
guidedOnboarding: GuidedOnboardingPluginStart;
lens: LensPublicStart;
@ -60,23 +63,63 @@ export interface PluginsStart {
export class EnterpriseSearchPlugin implements Plugin {
private config: ClientConfigType;
private hasInitialized: boolean = false;
private data: ClientData = {} as ClientData;
constructor(initializerContext: PluginInitializerContext) {
this.config = initializerContext.config.get<ClientConfigType>();
}
private data: ClientData = {} as ClientData;
private async getInitialData(http: HttpSetup) {
if (!this.config.host && this.config.canDeployEntSearch) return; // No API to call
if (this.hasInitialized) return; // We've already made an initial call
try {
this.data = await http.get('/internal/enterprise_search/config_data');
this.hasInitialized = true;
} catch (e) {
this.data.errorConnectingMessage = `${e.response.status} ${e.message}`;
}
}
private async getKibanaDeps(
core: CoreSetup,
params: AppMountParameters,
cloudSetup?: CloudSetup
) {
// Helper for using start dependencies on mount (instead of setup dependencies)
// and for grouping Kibana-related args together (vs. plugin-specific args)
const [coreStart, pluginsStart] = await core.getStartServices();
const cloud =
cloudSetup && (pluginsStart as PluginsStart).cloud
? { ...cloudSetup, ...(pluginsStart as PluginsStart).cloud }
: undefined;
const plugins = { ...pluginsStart, cloud } as PluginsStart;
coreStart.chrome
.getChromeStyle$()
.subscribe((style) => (this.isSidebarEnabled = style === 'classic'));
return { core: coreStart, isSidebarEnabled: this.isSidebarEnabled, params, plugins };
}
private getPluginData() {
// Small helper for grouping plugin data related args together
return { config: this.config, data: this.data, isSidebarEnabled: this.isSidebarEnabled };
}
private hasInitialized: boolean = false;
private isSidebarEnabled = true;
public setup(core: CoreSetup, plugins: PluginsSetup) {
const { cloud } = plugins;
const { config } = this;
core.application.register({
id: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.ID,
title: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.NAV_TITLE,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
appRoute: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
id: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.ID,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
const { chrome, http } = kibanaDeps.core;
@ -92,14 +135,14 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(EnterpriseSearchOverview, kibanaDeps, pluginData);
},
title: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.NAV_TITLE,
});
core.application.register({
id: ENTERPRISE_SEARCH_CONTENT_PLUGIN.ID,
title: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAV_TITLE,
euiIconType: ENTERPRISE_SEARCH_CONTENT_PLUGIN.LOGO,
appRoute: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
euiIconType: ENTERPRISE_SEARCH_CONTENT_PLUGIN.LOGO,
id: ENTERPRISE_SEARCH_CONTENT_PLUGIN.ID,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
const { chrome, http } = kibanaDeps.core;
@ -115,16 +158,14 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(EnterpriseSearchContent, kibanaDeps, pluginData);
},
title: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAV_TITLE,
});
core.application.register({
id: ANALYTICS_PLUGIN.ID,
title: ANALYTICS_PLUGIN.NAME,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
searchable: true,
navLinkStatus: AppNavLinkStatus.default,
appRoute: ANALYTICS_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
id: ANALYTICS_PLUGIN.ID,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
const { chrome, http } = kibanaDeps.core;
@ -138,14 +179,16 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(Analytics, kibanaDeps, pluginData);
},
navLinkStatus: AppNavLinkStatus.default,
searchable: true,
title: ANALYTICS_PLUGIN.NAME,
});
core.application.register({
id: ELASTICSEARCH_PLUGIN.ID,
title: ELASTICSEARCH_PLUGIN.NAME,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
appRoute: ELASTICSEARCH_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
id: ELASTICSEARCH_PLUGIN.ID,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
const { chrome, http } = kibanaDeps.core;
@ -159,15 +202,15 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(Elasticsearch, kibanaDeps, pluginData);
},
title: ELASTICSEARCH_PLUGIN.NAME,
});
if (config.canDeployEntSearch) {
core.application.register({
id: APP_SEARCH_PLUGIN.ID,
title: APP_SEARCH_PLUGIN.NAME,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
appRoute: APP_SEARCH_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
id: APP_SEARCH_PLUGIN.ID,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
const { chrome, http } = kibanaDeps.core;
@ -181,14 +224,14 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(AppSearch, kibanaDeps, pluginData);
},
title: APP_SEARCH_PLUGIN.NAME,
});
core.application.register({
id: WORKPLACE_SEARCH_PLUGIN.ID,
title: WORKPLACE_SEARCH_PLUGIN.NAME,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
appRoute: WORKPLACE_SEARCH_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
id: WORKPLACE_SEARCH_PLUGIN.ID,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
const { chrome, http } = kibanaDeps.core;
@ -205,15 +248,15 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(WorkplaceSearch, kibanaDeps, pluginData);
},
title: WORKPLACE_SEARCH_PLUGIN.NAME,
});
}
core.application.register({
id: SEARCH_EXPERIENCES_PLUGIN.ID,
title: SEARCH_EXPERIENCES_PLUGIN.NAME,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
appRoute: SEARCH_EXPERIENCES_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO,
id: SEARCH_EXPERIENCES_PLUGIN.ID,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
const { chrome, http } = kibanaDeps.core;
@ -227,68 +270,69 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(SearchExperiences, kibanaDeps, pluginData);
},
title: SEARCH_EXPERIENCES_PLUGIN.NAME,
});
if (plugins.home) {
plugins.home.featureCatalogue.registerSolution({
id: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.ID,
title: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.NAME,
icon: 'logoEnterpriseSearch',
description: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.DESCRIPTION,
path: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.URL,
icon: 'logoEnterpriseSearch',
id: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.ID,
order: 100,
path: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.URL,
title: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.NAME,
});
plugins.home.featureCatalogue.register({
id: ANALYTICS_PLUGIN.ID,
title: ANALYTICS_PLUGIN.NAME,
icon: 'appAnalytics',
description: ANALYTICS_PLUGIN.DESCRIPTION,
path: ANALYTICS_PLUGIN.URL,
category: 'data',
description: ANALYTICS_PLUGIN.DESCRIPTION,
icon: 'appAnalytics',
id: ANALYTICS_PLUGIN.ID,
path: ANALYTICS_PLUGIN.URL,
showOnHomePage: false,
title: ANALYTICS_PLUGIN.NAME,
});
if (config.canDeployEntSearch) {
plugins.home.featureCatalogue.register({
id: APP_SEARCH_PLUGIN.ID,
title: APP_SEARCH_PLUGIN.NAME,
icon: 'appSearchApp',
description: APP_SEARCH_PLUGIN.DESCRIPTION,
path: APP_SEARCH_PLUGIN.URL,
category: 'data',
description: APP_SEARCH_PLUGIN.DESCRIPTION,
icon: 'appSearchApp',
id: APP_SEARCH_PLUGIN.ID,
path: APP_SEARCH_PLUGIN.URL,
showOnHomePage: false,
title: APP_SEARCH_PLUGIN.NAME,
});
plugins.home.featureCatalogue.register({
id: WORKPLACE_SEARCH_PLUGIN.ID,
title: WORKPLACE_SEARCH_PLUGIN.NAME,
icon: 'workplaceSearchApp',
description: WORKPLACE_SEARCH_PLUGIN.DESCRIPTION,
path: WORKPLACE_SEARCH_PLUGIN.URL,
category: 'data',
description: WORKPLACE_SEARCH_PLUGIN.DESCRIPTION,
icon: 'workplaceSearchApp',
id: WORKPLACE_SEARCH_PLUGIN.ID,
path: WORKPLACE_SEARCH_PLUGIN.URL,
showOnHomePage: false,
title: WORKPLACE_SEARCH_PLUGIN.NAME,
});
}
plugins.home.featureCatalogue.register({
id: ELASTICSEARCH_PLUGIN.ID,
title: ELASTICSEARCH_PLUGIN.NAME,
icon: 'appElasticsearch',
description: ELASTICSEARCH_PLUGIN.DESCRIPTION,
path: ELASTICSEARCH_PLUGIN.URL,
category: 'data',
description: ELASTICSEARCH_PLUGIN.DESCRIPTION,
icon: 'appElasticsearch',
id: ELASTICSEARCH_PLUGIN.ID,
path: ELASTICSEARCH_PLUGIN.URL,
showOnHomePage: false,
title: ELASTICSEARCH_PLUGIN.NAME,
});
plugins.home.featureCatalogue.register({
id: SEARCH_EXPERIENCES_PLUGIN.ID,
title: SEARCH_EXPERIENCES_PLUGIN.NAME,
icon: 'logoEnterpriseSearch',
description: SEARCH_EXPERIENCES_PLUGIN.DESCRIPTION,
path: SEARCH_EXPERIENCES_PLUGIN.URL,
category: 'data',
description: SEARCH_EXPERIENCES_PLUGIN.DESCRIPTION,
icon: 'logoEnterpriseSearch',
id: SEARCH_EXPERIENCES_PLUGIN.ID,
path: SEARCH_EXPERIENCES_PLUGIN.URL,
showOnHomePage: false,
title: SEARCH_EXPERIENCES_PLUGIN.NAME,
});
}
}
@ -300,38 +344,4 @@ export class EnterpriseSearchPlugin implements Plugin {
}
public stop() {}
private async getKibanaDeps(
core: CoreSetup,
params: AppMountParameters,
cloudSetup?: CloudSetup
) {
// Helper for using start dependencies on mount (instead of setup dependencies)
// and for grouping Kibana-related args together (vs. plugin-specific args)
const [coreStart, pluginsStart] = await core.getStartServices();
const cloud =
cloudSetup && (pluginsStart as PluginsStart).cloud
? { ...cloudSetup, ...(pluginsStart as PluginsStart).cloud }
: undefined;
const plugins = { ...pluginsStart, cloud } as PluginsStart;
return { params, core: coreStart, plugins };
}
private getPluginData() {
// Small helper for grouping plugin data related args together
return { config: this.config, data: this.data };
}
private async getInitialData(http: HttpSetup) {
if (!this.config.host && this.config.canDeployEntSearch) return; // No API to call
if (this.hasInitialized) return; // We've already made an initial call
try {
this.data = await http.get('/internal/enterprise_search/config_data');
this.hasInitialized = true;
} catch (e) {
this.data.errorConnectingMessage = `${e.response.status} ${e.message}`;
}
}
}

View file

@ -0,0 +1,2 @@
/build
/target

View file

@ -0,0 +1,3 @@
# Serverless Search project plugin
This plugin contains configuration and code used to create a Serverless Search project. It leverages universal configuration and other APIs in the [`serverless`](../serverless/README.mdx) plugin to configure Kibana.

View file

@ -0,0 +1,9 @@
/*
* 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.
*/
export const PLUGIN_ID = 'serverlessSearch';
export const PLUGIN_NAME = 'serverlessSearch';

View file

@ -0,0 +1,23 @@
{
"type": "plugin",
"id": "@kbn/serverless-search",
"owner": "@elastic/appex-sharedux",
"description": "Serverless customizations for search.",
"plugin": {
"id": "serverlessSearch",
"server": true,
"browser": true,
"configPath": [
"xpack",
"serverless",
"search"
],
"requiredPlugins": [
"serverless",
"enterpriseSearch",
"management"
],
"optionalPlugins": [],
"requiredBundles": []
}
}

View file

@ -0,0 +1,11 @@
{
"name": "@kbn/serverless-search",
"version": "1.0.0",
"license": "Elastic License 2.0",
"private": true,
"scripts": {
"build": "yarn plugin-helpers build",
"plugin-helpers": "node ../../../scripts/plugin_helpers",
"kbn": "node ../../../scripts/kbn"
}
}

View file

@ -0,0 +1,16 @@
/*
* 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 { ServerlessSearchPlugin } from './plugin';
// This exports static code and TypeScript types,
// as well as, Kibana Platform `plugin()` initializer.
export function plugin() {
return new ServerlessSearchPlugin();
}
export type { ServerlessSearchPluginSetup, ServerlessSearchPluginStart } from './types';

View file

@ -0,0 +1,34 @@
/*
* 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import {
ServerlessSearchPluginSetup,
ServerlessSearchPluginSetupDependencies,
ServerlessSearchPluginStart,
ServerlessSearchPluginStartDependencies,
} from './types';
export class ServerlessSearchPlugin
implements Plugin<ServerlessSearchPluginSetup, ServerlessSearchPluginStart>
{
public setup(
_core: CoreSetup,
_setupDeps: ServerlessSearchPluginSetupDependencies
): ServerlessSearchPluginSetup {
return {};
}
public start(
_core: CoreStart,
_startDeps: ServerlessSearchPluginStartDependencies
): ServerlessSearchPluginStart {
return {};
}
public stop() {}
}

View file

@ -0,0 +1,31 @@
/*
* 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 { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public';
import { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public';
import {
EnterpriseSearchPublicSetup,
EnterpriseSearchPublicStart,
} from '@kbn/enterprise-search-plugin/public';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ServerlessSearchPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ServerlessSearchPluginStart {}
export interface ServerlessSearchPluginSetupDependencies {
enterpriseSearch: EnterpriseSearchPublicSetup;
management: ManagementSetup;
serverless: ServerlessPluginSetup;
}
export interface ServerlessSearchPluginStartDependencies {
enterpriseSearch: EnterpriseSearchPublicStart;
management: ManagementStart;
serverless: ServerlessPluginStart;
}

View file

@ -0,0 +1,23 @@
/*
* 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 { schema, TypeOf } from '@kbn/config-schema';
import { PluginConfigDescriptor } from '@kbn/core/server';
export * from './types';
const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: false }),
});
type ConfigType = TypeOf<typeof configSchema>;
export const config: PluginConfigDescriptor<ConfigType> = {
schema: configSchema,
};
export type ServerlessSearchConfig = TypeOf<typeof configSchema>;

View file

@ -0,0 +1,20 @@
/*
* 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 { PluginInitializerContext } from '@kbn/core/server';
import { ServerlessSearchPlugin } from './plugin';
export { config } from './config';
// This exports static code and TypeScript types,
// as well as, Kibana Platform `plugin()` initializer.
export function plugin(initializerContext: PluginInitializerContext) {
return new ServerlessSearchPlugin(initializerContext);
}
export type { ServerlessSearchPluginSetup, ServerlessSearchPluginStart } from './types';

View file

@ -0,0 +1,26 @@
/*
* 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 { PluginInitializerContext, Plugin } from '@kbn/core/server';
import { ServerlessSearchPluginSetup, ServerlessSearchPluginStart } from './types';
export class ServerlessSearchPlugin
implements Plugin<ServerlessSearchPluginSetup, ServerlessSearchPluginStart>
{
constructor(_initializerContext: PluginInitializerContext) {}
public setup() {
return {};
}
public start() {
return {};
}
public stop() {}
}

View file

@ -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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ServerlessSearchPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ServerlessSearchPluginStart {}

View file

@ -0,0 +1,24 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": [
"index.ts",
"common/**/*.ts",
"public/**/*.ts",
"public/**/*.tsx",
"server/**/*.ts",
"../../../typings/**/*"
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/core",
"@kbn/config-schema",
"@kbn/enterprise-search-plugin",
"@kbn/management-plugin",
"@kbn/serverless",
]
}

View file

@ -5029,6 +5029,10 @@
version "0.0.0"
uid ""
"@kbn/serverless-search@link:x-pack/plugins/serverless_search":
version "0.0.0"
uid ""
"@kbn/serverless@link:x-pack/plugins/serverless":
version "0.0.0"
uid ""