Search platform context cleanup (#57448) (#58006)

* Initial commit - cleanup

* cleanup

* tsing

* ts fixes

* Fix jest test

* Code review fxes

* Remove empty file reference

Remove empty file reference

* code review

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Liza Katz 2020-02-19 19:07:57 +00:00 committed by GitHub
parent 71bda5f289
commit 71401c62df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 140 additions and 521 deletions

View file

@ -18,11 +18,7 @@
*/
import { Observable } from 'rxjs';
import {
ISearchContext,
SYNC_SEARCH_STRATEGY,
ISearchGeneric,
} from '../../../src/plugins/data/public';
import { ISearchContext, SYNC_SEARCH_STRATEGY } from '../../../src/plugins/data/public';
import { TSearchStrategyProvider, ISearchStrategy } from '../../../src/plugins/data/public';
import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common';
@ -54,15 +50,15 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common';
* @param search - a search function to access other strategies that have already been registered.
*/
export const demoClientSearchStrategyProvider: TSearchStrategyProvider<typeof DEMO_SEARCH_STRATEGY> = (
context: ISearchContext,
search: ISearchGeneric
context: ISearchContext
): ISearchStrategy<typeof DEMO_SEARCH_STRATEGY> => {
const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
const { search } = syncStrategyProvider(context);
return {
search: (request, options) =>
search(
{ ...request, serverStrategy: DEMO_SEARCH_STRATEGY },
options,
SYNC_SEARCH_STRATEGY
) as Observable<IDemoResponse>,
search: (request, options) => {
return search({ ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, options) as Observable<
IDemoResponse
>;
},
};
};

View file

@ -17,12 +17,10 @@
* under the License.
*/
import { PluginInitializer, PluginInitializerContext } from 'kibana/public';
import { PluginInitializer } from 'kibana/public';
import { DemoDataPlugin } from './plugin';
export { DEMO_SEARCH_STRATEGY } from '../common';
export const plugin: PluginInitializer<void, void> = (
initializerContext: PluginInitializerContext
) => new DemoDataPlugin(initializerContext);
export const plugin: PluginInitializer<void, void> = () => new DemoDataPlugin();

View file

@ -18,7 +18,7 @@
*/
import { DataPublicPluginSetup } from '../../../src/plugins/data/public';
import { Plugin, CoreSetup, PluginInitializerContext } from '../../../src/core/public';
import { Plugin, CoreSetup } from '../../../src/core/public';
import { DEMO_SEARCH_STRATEGY } from '../common';
import { demoClientSearchStrategyProvider } from './demo_search_strategy';
import { IDemoRequest, IDemoResponse } from '../common';
@ -47,10 +47,8 @@ declare module '../../../src/plugins/data/public' {
}
export class DemoDataPlugin implements Plugin {
constructor(private initializerContext: PluginInitializerContext) {}
public setup(core: CoreSetup, deps: DemoDataSearchSetupDependencies) {
deps.data.search.registerSearchStrategyProvider(
this.initializerContext.opaqueId,
DEMO_SEARCH_STRATEGY,
demoClientSearchStrategyProvider
);

View file

@ -28,12 +28,13 @@ import {
EuiSideNav,
} from '@elastic/eui';
import { AppMountContext, AppMountParameters } from '../../../src/core/public';
import { AppMountParameters, CoreStart } from '../../../src/core/public';
import { EsSearchTest } from './es_strategy';
import { Page } from './page';
import { DemoStrategy } from './demo_strategy';
import { DocumentationPage } from './documentation';
import { SearchApiPage } from './search_api';
import { AppPluginStartDependencies, SearchBarComponentParams } from './types';
const Home = () => <DocumentationPage />;
@ -44,7 +45,7 @@ interface PageDef {
}
type NavProps = RouteComponentProps & {
navigateToApp: AppMountContext['core']['application']['navigateToApp'];
navigateToApp: CoreStart['application']['navigateToApp'];
pages: PageDef[];
};
@ -71,7 +72,7 @@ const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => {
const buildPage = (page: PageDef) => <Page title={page.title}>{page.component}</Page>;
const SearchApp = ({ basename, context }: { basename: string; context: AppMountContext }) => {
const SearchApp = ({ basename, data, application }: SearchBarComponentParams) => {
const pages: PageDef[] = [
{
id: 'home',
@ -86,12 +87,12 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC
{
title: 'ES search strategy',
id: 'esSearch',
component: <EsSearchTest search={context.search!.search} />,
component: <EsSearchTest search={data.search.search} />,
},
{
title: 'Demo search strategy',
id: 'demoSearch',
component: <DemoStrategy search={context.search!.search} />,
component: <DemoStrategy search={data.search.search} />,
},
];
@ -103,7 +104,7 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC
<Router basename={basename}>
<EuiPage>
<EuiPageSideBar>
<Nav navigateToApp={context.core.application.navigateToApp} pages={pages} />
<Nav navigateToApp={application.navigateToApp} pages={pages} />
</EuiPageSideBar>
<Route path="/" exact component={Home} />
{routes}
@ -113,10 +114,14 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC
};
export const renderApp = (
context: AppMountContext,
coreStart: CoreStart,
deps: AppPluginStartDependencies,
{ appBasePath, element }: AppMountParameters
) => {
ReactDOM.render(<SearchApp basename={appBasePath} context={context} />, element);
ReactDOM.render(
<SearchApp basename={appBasePath} data={deps.data} application={coreStart.application} />,
element
);
return () => ReactDOM.unmountComponentAtNode(element);
};

View file

@ -38,8 +38,6 @@ import serverPlugin from '!!raw-loader!./../../../src/plugins/data/server/search
// @ts-ignore
import serverStrategy from '!!raw-loader!./../../../src/plugins/data/server/search/es_search/es_search_strategy';
// @ts-ignore
import publicPlugin from '!!raw-loader!./../../../src/plugins/data/public/search/es_search/es_search_service';
// @ts-ignore
import publicStrategy from '!!raw-loader!./../../../src/plugins/data/public/search/es_search/es_search_strategy';
@ -125,10 +123,7 @@ export class EsSearchTest extends React.Component<Props, State> {
codeSections={[
{
title: 'Public',
code: [
{ description: 'es_search_service.ts', snippet: publicPlugin },
{ description: 'es_search_strategy.ts', snippet: publicStrategy },
],
code: [{ description: 'es_search_strategy.ts', snippet: publicStrategy }],
},
{
title: 'Server',

View file

@ -17,22 +17,18 @@
* under the License.
*/
import { Plugin, CoreSetup } from 'kibana/public';
import { ISearchAppMountContext } from '../../../src/plugins/data/public';
import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
import { AppPluginStartDependencies } from './types';
declare module 'kibana/public' {
interface AppMountContext {
search?: ISearchAppMountContext;
}
}
export class SearchExplorerPlugin implements Plugin {
public setup(core: CoreSetup) {
public setup(core: CoreSetup<AppPluginStartDependencies>) {
core.application.register({
id: 'searchExplorer',
title: 'Search Explorer',
async mount(context, params) {
async mount(params: AppMountParameters) {
const [coreStart, depsStart] = await core.getStartServices();
const { renderApp } = await import('./application');
return renderApp(context, params);
return renderApp(coreStart, depsStart, params);
},
});
}

View file

@ -19,10 +19,6 @@
import React from 'react';
import { GuideSection } from './guide_section';
// @ts-ignore
import publicSetupContract from '!!raw-loader!./../../../src/plugins/data/public/search/i_search_setup';
// @ts-ignore
import publicSearchStrategy from '!!raw-loader!./../../../src/plugins/data/public/search/i_search_strategy';
// @ts-ignore
import publicSearch from '!!raw-loader!./../../../src/plugins/data/public/search/i_search';
// @ts-ignore
@ -31,8 +27,6 @@ import publicPlugin from '!!raw-loader!./../../../src/plugins/data/public/search
// @ts-ignore
import serverSetupContract from '!!raw-loader!./../../../src/plugins/data/server/search/i_search_setup';
// @ts-ignore
import serverSearchStrategy from '!!raw-loader!./../../../src/plugins/data/server/search/i_search_strategy';
// @ts-ignore
import serverSearch from '!!raw-loader!./../../../src/plugins/data/server/search/i_search';
// @ts-ignore
import serverPlugin from '!!raw-loader!./../../../src/plugins/data/server/search/search_service';
@ -47,18 +41,10 @@ export const SearchApiPage = () => (
description: 'search_service.ts',
snippet: publicPlugin,
},
{
description: `i_search_setup.ts`,
snippet: publicSetupContract,
},
{
description: 'i_search',
snippet: publicSearch,
},
{
description: 'i_search_strategy',
snippet: publicSearchStrategy,
},
],
},
{
@ -76,10 +62,6 @@ export const SearchApiPage = () => (
description: 'i_search',
snippet: serverSearch,
},
{
description: 'i_search_strategy',
snippet: serverSearchStrategy,
},
],
},
]}

View file

@ -17,8 +17,15 @@
* under the License.
*/
import { ISearchGeneric } from './i_search';
import { CoreStart } from 'kibana/public';
import { DataPublicPluginStart } from '../../../src/plugins/data/public';
export interface ISearchAppMountContext {
search: ISearchGeneric;
export interface AppPluginStartDependencies {
data: DataPublicPluginStart;
}
export interface SearchBarComponentParams {
application: CoreStart['application'];
basename: string;
data: DataPublicPluginStart;
}

View file

@ -19,7 +19,6 @@
import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public';
import { ISearchAppMountContext } from '../../../src/plugins/data/public';
import {
PHONE_TRIGGER,
USER_TRIGGER,
@ -38,12 +37,6 @@ import {
SHOWCASE_PLUGGABILITY_ACTION,
} from './actions/actions';
declare module 'kibana/public' {
interface AppMountContext {
search?: ISearchAppMountContext;
}
}
interface StartDeps {
uiActions: UiActionsStart;
}

View file

@ -57,7 +57,7 @@ export class DataPublicPlugin implements Plugin<DataPublicPluginSetup, DataPubli
private readonly packageInfo: PackageInfo;
constructor(initializerContext: PluginInitializerContext) {
this.searchService = new SearchService(initializerContext);
this.searchService = new SearchService();
this.queryService = new QueryService();
this.fieldFormatsService = new FieldFormatsService();
this.storage = new Storage(window.localStorage);

View file

@ -1,71 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { createAppMountSearchContext } from './create_app_mount_context_search';
import { from } from 'rxjs';
describe('Create app mount search context', () => {
it('Returns search fn when there are no strategies', () => {
const context = createAppMountSearchContext({});
expect(context.search).toBeDefined();
});
it(`Search throws an error when the strategy doesn't exist`, () => {
const context = createAppMountSearchContext({});
expect(() => context.search({}, {}, 'noexist').toPromise()).toThrowErrorMatchingInlineSnapshot(
`"Strategy with name noexist does not exist"`
);
});
it(`Search fn is called on appropriate strategy name`, done => {
const context = createAppMountSearchContext({
mysearch: search =>
Promise.resolve({
search: () => from(Promise.resolve({ percentComplete: 98 })),
}),
anothersearch: search =>
Promise.resolve({
search: () => from(Promise.resolve({ percentComplete: 0 })),
}),
});
context.search({}, {}, 'mysearch').subscribe(response => {
expect(response).toEqual({ percentComplete: 98 });
done();
});
});
it(`Search fn is called with the passed in request object`, done => {
const context = createAppMountSearchContext({
mysearch: search => {
return Promise.resolve({
search: request => {
expect(request).toEqual({ greeting: 'hi' });
return from(Promise.resolve({}));
},
});
},
});
context.search({ greeting: 'hi' } as any, {}, 'mysearch').subscribe(
response => {},
() => {},
done
);
});
});

View file

@ -1,53 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { mergeMap } from 'rxjs/operators';
import { from } from 'rxjs';
import { ISearchAppMountContext } from './i_search_app_mount_context';
import { ISearchGeneric } from './i_search';
import {
TSearchStrategiesMap,
ISearchStrategy,
TSearchStrategyProviderEnhanced,
} from './i_search_strategy';
import { TStrategyTypes } from './strategy_types';
import { DEFAULT_SEARCH_STRATEGY } from '../../common/search';
export const createAppMountSearchContext = (
searchStrategies: TSearchStrategiesMap
): ISearchAppMountContext => {
const getSearchStrategy = <K extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY>(
strategyName?: K
): Promise<ISearchStrategy<K>> => {
const strategyProvider = searchStrategies[
strategyName ? strategyName : DEFAULT_SEARCH_STRATEGY
] as TSearchStrategyProviderEnhanced<K> | undefined;
if (!strategyProvider) {
throw new Error(`Strategy with name ${strategyName} does not exist`);
}
return strategyProvider(search);
};
const search: ISearchGeneric = (request, options, strategyName) => {
const strategyPromise = getSearchStrategy(strategyName);
return from(strategyPromise).pipe(mergeMap(strategy => strategy.search(request, options)));
};
return { search };
};

View file

@ -1,42 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { coreMock } from '../../../../../core/public/mocks';
import { EsSearchService } from './es_search_service';
import { CoreSetup } from '../../../../../core/public';
import { searchSetupMock } from '../mocks';
describe('ES search strategy service', () => {
let service: EsSearchService;
let mockCoreSetup: MockedKeys<CoreSetup>;
beforeEach(() => {
service = new EsSearchService(coreMock.createPluginInitializerContext());
mockCoreSetup = coreMock.createSetup();
});
describe('setup()', () => {
it('registers the ES search strategy', async () => {
service.setup(mockCoreSetup, {
search: searchSetupMock,
});
expect(searchSetupMock.registerSearchStrategyProvider).toBeCalled();
});
});
});

View file

@ -1,41 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Plugin, CoreSetup, PluginInitializerContext } from '../../../../../core/public';
import { ES_SEARCH_STRATEGY } from '../../../common/search/es_search';
import { esSearchStrategyProvider } from './es_search_strategy';
import { ISearchSetup } from '../i_search_setup';
export interface IEsSearchSetupDependencies {
search: ISearchSetup;
}
export class EsSearchService implements Plugin {
constructor(private initializerContext: PluginInitializerContext) {}
public setup(core: CoreSetup, deps: IEsSearchSetupDependencies) {
deps.search.registerSearchStrategyProvider(
this.initializerContext.opaqueId,
ES_SEARCH_STRATEGY,
esSearchStrategyProvider
);
}
public start() {}
public stop() {}
}

View file

@ -19,15 +19,15 @@
import { coreMock } from '../../../../../core/public/mocks';
import { esSearchStrategyProvider } from './es_search_strategy';
import { CoreSetup } from 'kibana/public';
import { CoreStart } from 'kibana/public';
import { ES_SEARCH_STRATEGY } from '../../../common/search/es_search';
describe('ES search strategy', () => {
let mockCoreSetup: MockedKeys<CoreSetup>;
let mockCoreStart: MockedKeys<CoreStart>;
const mockSearch = jest.fn();
beforeEach(() => {
mockCoreSetup = coreMock.createSetup();
mockCoreStart = coreMock.createStart();
mockSearch.mockClear();
});
@ -35,12 +35,16 @@ describe('ES search strategy', () => {
const request = { params: {} };
const options = {};
const esSearch = esSearchStrategyProvider(
{
core: mockCoreSetup,
},
mockSearch
);
const esSearch = esSearchStrategyProvider({
core: mockCoreStart,
getSearchStrategy: jest.fn().mockImplementation(() => {
return () => {
return {
search: mockSearch,
};
};
}),
});
esSearch.search(request, options);
expect(mockSearch.mock.calls[0][0]).toEqual({

View file

@ -21,11 +21,10 @@ import { Observable } from 'rxjs';
import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../common/search';
import { SYNC_SEARCH_STRATEGY } from '../sync_search_strategy';
import { getEsPreference } from './get_es_preference';
import { TSearchStrategyProvider, ISearchStrategy, ISearchGeneric, ISearchContext } from '..';
import { ISearchContext, TSearchStrategyProvider, ISearchStrategy } from '../types';
export const esSearchStrategyProvider: TSearchStrategyProvider<typeof ES_SEARCH_STRATEGY> = (
context: ISearchContext,
search: ISearchGeneric
context: ISearchContext
): ISearchStrategy<typeof ES_SEARCH_STRATEGY> => {
return {
search: (request, options) => {
@ -34,10 +33,10 @@ export const esSearchStrategyProvider: TSearchStrategyProvider<typeof ES_SEARCH_
const customPreference = context.core.uiSettings.get('courier:customRequestPreference');
request.params.preference = getEsPreference(setPreference, customPreference);
}
return search(
const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
return syncStrategyProvider(context).search(
{ ...request, serverStrategy: ES_SEARCH_STRATEGY },
options,
SYNC_SEARCH_STRATEGY
options
) as Observable<IEsSearchResponse>;
},
};

View file

@ -1,25 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { coreMock } from '../../../../../../src/core/public/mocks';
import { esSearchService } from '.';
it('es search service is instantiated', () => {
const esSearch = esSearchService(coreMock.createPluginInitializerContext());
expect(esSearch).toBeDefined();
});

View file

@ -1,25 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { PluginInitializer, PluginInitializerContext } from 'kibana/public';
import { EsSearchService } from './es_search_service';
export const esSearchService: PluginInitializer<void, void> = (
initializerContext: PluginInitializerContext
) => new EsSearchService(initializerContext);

View file

@ -1,23 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { CoreSetup } from '../../../../core/public';
export interface ISearchContext {
core: CoreSetup;
}

View file

@ -1,40 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { IContextProvider } from 'kibana/public';
import { ISearchContext } from './i_search_context';
import { TRegisterSearchStrategyProvider, TSearchStrategyProvider } from './i_search_strategy';
/**
* The setup contract exposed by the Search plugin exposes the search strategy extension
* point.
*/
export interface ISearchSetup {
registerSearchStrategyContext: <TContextName extends keyof ISearchContext>(
pluginId: symbol,
contextName: TContextName,
provider: IContextProvider<TSearchStrategyProvider<any>, TContextName>
) => void;
/**
* Extension point exposed for other plugins to register their own search
* strategies.
*/
registerSearchStrategyProvider: TRegisterSearchStrategyProvider;
}

View file

@ -17,12 +17,13 @@
* under the License.
*/
export { ISearchAppMountContext } from './i_search_app_mount_context';
export { ISearchSetup } from './i_search_setup';
export { ISearchStart } from './search_service';
export { ISearchContext } from './i_search_context';
export {
ISearchSetup,
ISearchStart,
ISearchContext,
TSearchStrategyProvider,
ISearchStrategy,
} from './types';
export {
ISearch,
@ -32,8 +33,6 @@ export {
ISearchGeneric,
} from './i_search';
export { TSearchStrategyProvider, ISearchStrategy } from './i_search_strategy';
export { IEsSearchResponse, IEsSearchRequest, ES_SEARCH_STRATEGY } from '../../common/search';
export { SYNC_SEARCH_STRATEGY } from './sync_search_strategy';

View file

@ -27,7 +27,7 @@ describe('Search service', () => {
let mockCoreSetup: MockedKeys<CoreSetup>;
beforeEach(() => {
searchService = new SearchService(coreMock.createPluginInitializerContext());
searchService = new SearchService();
mockCoreSetup = coreMock.createSetup();
});
@ -36,7 +36,6 @@ describe('Search service', () => {
const setup = searchService.setup(mockCoreSetup, {
version: '8',
} as any);
expect(setup).toHaveProperty('registerSearchStrategyContext');
expect(setup).toHaveProperty('registerSearchStrategyProvider');
});
});

View file

@ -16,46 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
Plugin,
CoreSetup,
PluginInitializerContext,
CoreStart,
IContextContainer,
PluginOpaqueId,
PackageInfo,
} from '../../../../core/public';
import { ISearchAppMountContext } from './i_search_app_mount_context';
import { ISearchSetup } from './i_search_setup';
import { createAppMountSearchContext } from './create_app_mount_context_search';
import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public';
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
import {
TSearchStrategyProvider,
TRegisterSearchStrategyProvider,
TSearchStrategiesMap,
} from './i_search_strategy';
import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types';
import { TStrategyTypes } from './strategy_types';
import { esSearchService } from './es_search';
import { ISearchGeneric } from './i_search';
import { getEsClient, LegacyApiCaller } from './es_client';
/**
* Extends the AppMountContext so other plugins have access
* to search functionality in their applications.
*/
declare module 'kibana/public' {
interface AppMountContext {
search?: ISearchAppMountContext;
}
}
export interface ISearchStart {
search: ISearchGeneric;
__LEGACY: {
esClient: LegacyApiCaller;
};
}
import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search';
import { esSearchStrategyProvider } from './es_search/es_search_strategy';
/**
* The search plugin exposes two registration methods for other plugins:
@ -73,63 +42,43 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
*/
private searchStrategies: TSearchStrategiesMap = {};
/**
* Exposes context to the search strategies.
*/
private contextContainer?: IContextContainer<TSearchStrategyProvider<any>>;
private esClient?: LegacyApiCaller;
private search?: ISearchGeneric;
constructor(private initializerContext: PluginInitializerContext) {}
private registerSearchStrategyProvider = <T extends TStrategyTypes>(
name: T,
strategyProvider: TSearchStrategyProvider<T>
) => {
this.searchStrategies[name] = strategyProvider;
};
private getSearchStrategy = <T extends TStrategyTypes>(name: T): TSearchStrategyProvider<T> => {
const strategyProvider = this.searchStrategies[name];
if (!strategyProvider) throw new Error(`Search strategy ${name} not found`);
return strategyProvider;
};
public setup(core: CoreSetup, packageInfo: PackageInfo): ISearchSetup {
const search = (this.search = createAppMountSearchContext(this.searchStrategies).search);
core.application.registerMountContext<'search'>('search', () => {
return { search };
});
this.contextContainer = core.context.createContextContainer();
this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
const registerSearchStrategyProvider: TRegisterSearchStrategyProvider = <
T extends TStrategyTypes
>(
plugin: PluginOpaqueId,
name: T,
strategyProvider: TSearchStrategyProvider<T>
) => {
this.searchStrategies[name] = this.contextContainer!.createHandler(plugin, strategyProvider);
this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider);
this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider);
return {
registerSearchStrategyProvider: this.registerSearchStrategyProvider,
};
const api = {
registerSearchStrategyContext: this.contextContainer!.registerContext,
registerSearchStrategyProvider,
__LEGACY: {
esClient: this.esClient,
},
};
api.registerSearchStrategyContext(this.initializerContext.opaqueId, 'core', () => core);
api.registerSearchStrategyProvider(
this.initializerContext.opaqueId,
SYNC_SEARCH_STRATEGY,
syncSearchStrategyProvider
);
// ES search capabilities are written in a way that it could easily be a separate plugin,
// however these two plugins are tightly coupled due to the default search strategy using
// es search types.
esSearchService(this.initializerContext).setup(core, { search: api });
return api;
}
public start(core: CoreStart): ISearchStart {
if (!this.search) {
throw new Error('Search should always be defined');
}
return {
search: this.search,
search: (request, options, strategyName) => {
const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY);
const { search } = strategyProvider({
core,
getSearchStrategy: this.getSearchStrategy,
});
return search(request as any, options);
},
__LEGACY: {
esClient: this.esClient!,
},

View file

@ -19,32 +19,29 @@
import { coreMock } from '../../../../core/public/mocks';
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
import { CoreSetup } from '../../../../core/public';
import { CoreStart } from 'kibana/public';
describe('Sync search strategy', () => {
let mockCoreSetup: MockedKeys<CoreSetup>;
const mockSearch = jest.fn();
let mockCoreStart: MockedKeys<CoreStart>;
beforeEach(() => {
mockCoreSetup = coreMock.createSetup();
mockCoreStart = coreMock.createStart();
});
it('returns a strategy with `search` that calls the backend API', () => {
mockCoreSetup.http.fetch.mockImplementationOnce(() => Promise.resolve());
mockCoreStart.http.fetch.mockImplementationOnce(() => Promise.resolve());
const syncSearch = syncSearchStrategyProvider(
{
core: mockCoreSetup,
},
mockSearch
);
const syncSearch = syncSearchStrategyProvider({
core: mockCoreStart,
getSearchStrategy: jest.fn(),
});
syncSearch.search(
{
serverStrategy: SYNC_SEARCH_STRATEGY,
},
{}
);
expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toEqual({
expect(mockCoreStart.http.fetch.mock.calls[0][0]).toEqual({
path: `/internal/search/${SYNC_SEARCH_STRATEGY}`,
body: JSON.stringify({
serverStrategy: 'SYNC_SEARCH_STRATEGY',

View file

@ -19,9 +19,8 @@
import { BehaviorSubject, from } from 'rxjs';
import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../common/search';
import { ISearchContext } from './i_search_context';
import { ISearch, ISearchOptions } from './i_search';
import { TSearchStrategyProvider, ISearchStrategy } from './i_search_strategy';
import { TSearchStrategyProvider, ISearchStrategy, ISearchContext } from './types';
export const SYNC_SEARCH_STRATEGY = 'SYNC_SEARCH_STRATEGY';

View file

@ -17,9 +17,15 @@
* under the License.
*/
import { CoreStart } from 'kibana/public';
import { ISearch, ISearchGeneric } from './i_search';
import { TStrategyTypes } from './strategy_types';
import { ISearchContext } from './i_search_context';
import { LegacyApiCaller } from './es_client';
export interface ISearchContext {
core: CoreStart;
getSearchStrategy: <T extends TStrategyTypes>(name: T) => TSearchStrategyProvider<T>;
}
/**
* Search strategy interface contains a search method that takes in
@ -38,14 +44,17 @@ export type TSearchStrategyProviderEnhanced<T extends TStrategyTypes> = (
search: ISearchGeneric
) => Promise<ISearchStrategy<T>>;
export type TSearchStrategiesMap = {
[K in TStrategyTypes]?: TSearchStrategyProvider<any>;
};
/**
* Search strategy provider creates an instance of a search strategy with the request
* handler context bound to it. This way every search strategy can use
* whatever information they require from the request context.
*/
export type TSearchStrategyProvider<T extends TStrategyTypes> = (
context: ISearchContext,
search: ISearchGeneric
context: ISearchContext
) => ISearchStrategy<T>;
/**
@ -53,11 +62,25 @@ export type TSearchStrategyProvider<T extends TStrategyTypes> = (
* strategies.
*/
export type TRegisterSearchStrategyProvider = <T extends TStrategyTypes>(
opaqueId: symbol,
name: T,
searchStrategyProvider: TSearchStrategyProvider<T>
) => void;
export type TSearchStrategiesMap = {
[K in TStrategyTypes]?: TSearchStrategyProviderEnhanced<K>;
};
/**
* The setup contract exposed by the Search plugin exposes the search strategy extension
* point.
*/
export interface ISearchSetup {
/**
* Extension point exposed for other plugins to register their own search
* strategies.
*/
registerSearchStrategyProvider: TRegisterSearchStrategyProvider;
}
export interface ISearchStart {
search: ISearchGeneric;
__LEGACY: {
esClient: LegacyApiCaller;
};
}