From ddfed3ff6e55388f30006bd76051d92d15652356 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Sep 2019 16:24:55 -0400 Subject: [PATCH] Phase 1 of search services --- src/plugins/search/README.md | 13 ++ .../search/common/es_search/elasticsearch.ts | 172 ++++++++++++++++++ src/plugins/search/common/es_search/index.ts | 20 ++ src/plugins/search/common/es_search/types.ts | 31 ++++ src/plugins/search/common/index.ts | 26 +++ src/plugins/search/common/types.ts | 55 ++++++ src/plugins/search/kibana.json | 6 + src/plugins/search/public/constants.ts | 20 ++ .../public/create_app_mount_context_search.ts | 56 ++++++ .../public/es_search/es_search_strategy.ts | 43 +++++ src/plugins/search/public/es_search/index.ts | 25 +++ src/plugins/search/public/es_search/plugin.ts | 41 +++++ src/plugins/search/public/i_search.ts | 56 ++++++ .../public/i_search_app_mount_context.ts | 27 +++ src/plugins/search/public/i_search_context.ts | 23 +++ src/plugins/search/public/i_search_setup.ts | 41 +++++ .../search/public/i_search_strategy.ts | 67 +++++++ src/plugins/search/public/index.ts | 47 +++++ src/plugins/search/public/plugin.ts | 128 +++++++++++++ src/plugins/search/public/strategy_types.ts | 40 ++++ .../search/public/sync_search_strategy.ts | 55 ++++++ src/plugins/search/public/types.ts | 25 +++ src/plugins/search/server/create_api.ts | 48 +++++ .../search/server/es_search/elasticsearch.ts | 34 ++++ .../server/es_search/es_search_strategy.ts | 53 ++++++ src/plugins/search/server/es_search/index.ts | 27 +++ src/plugins/search/server/es_search/plugin.ts | 42 +++++ .../server/i_route_handler_search_context.ts | 24 +++ src/plugins/search/server/i_search.ts | 42 +++++ src/plugins/search/server/i_search_context.ts | 23 +++ src/plugins/search/server/i_search_setup.ts | 51 ++++++ .../search/server/i_search_strategy.ts | 70 +++++++ src/plugins/search/server/index.ts | 34 ++++ src/plugins/search/server/plugin.ts | 105 +++++++++++ src/plugins/search/server/routes.ts | 48 +++++ src/plugins/search/server/strategy_types.ts | 39 ++++ .../plugins/demo_search/common/index.ts | 34 ++++ .../plugins/demo_search/kibana.json | 10 + .../plugins/demo_search/package.json | 17 ++ .../public/demo_search_strategy.ts | 70 +++++++ .../plugins/demo_search/public/index.ts | 28 +++ .../plugins/demo_search/public/plugin.ts | 52 ++++++ .../plugins/demo_search/server/constants.ts | 20 ++ .../server/demo_search_strategy.ts | 36 ++++ .../plugins/demo_search/server/index.ts | 25 +++ .../plugins/demo_search/server/plugin.ts | 52 ++++++ .../plugins/demo_search/tsconfig.json | 15 ++ .../plugins/search_explorer/kibana.json | 10 + .../plugins/search_explorer/package.json | 17 ++ .../search_explorer/public/application.tsx | 122 +++++++++++++ .../search_explorer/public/demo_strategy.tsx | 134 ++++++++++++++ .../search_explorer/public/do_search.tsx | 139 ++++++++++++++ .../search_explorer/public/documentation.tsx | 102 +++++++++++ .../search_explorer/public/es_strategy.tsx | 146 +++++++++++++++ .../search_explorer/public/guide_section.tsx | 137 ++++++++++++++ .../plugins/search_explorer/public/index.ts | 22 +++ .../plugins/search_explorer/public/page.tsx | 51 ++++++ .../plugins/search_explorer/public/plugin.tsx | 42 +++++ .../search_explorer/public/search_api.tsx | 90 +++++++++ .../plugins/search_explorer/tsconfig.json | 15 ++ 60 files changed, 3043 insertions(+) create mode 100644 src/plugins/search/README.md create mode 100644 src/plugins/search/common/es_search/elasticsearch.ts create mode 100644 src/plugins/search/common/es_search/index.ts create mode 100644 src/plugins/search/common/es_search/types.ts create mode 100644 src/plugins/search/common/index.ts create mode 100644 src/plugins/search/common/types.ts create mode 100644 src/plugins/search/kibana.json create mode 100644 src/plugins/search/public/constants.ts create mode 100644 src/plugins/search/public/create_app_mount_context_search.ts create mode 100644 src/plugins/search/public/es_search/es_search_strategy.ts create mode 100644 src/plugins/search/public/es_search/index.ts create mode 100644 src/plugins/search/public/es_search/plugin.ts create mode 100644 src/plugins/search/public/i_search.ts create mode 100644 src/plugins/search/public/i_search_app_mount_context.ts create mode 100644 src/plugins/search/public/i_search_context.ts create mode 100644 src/plugins/search/public/i_search_setup.ts create mode 100644 src/plugins/search/public/i_search_strategy.ts create mode 100644 src/plugins/search/public/index.ts create mode 100644 src/plugins/search/public/plugin.ts create mode 100644 src/plugins/search/public/strategy_types.ts create mode 100644 src/plugins/search/public/sync_search_strategy.ts create mode 100644 src/plugins/search/public/types.ts create mode 100644 src/plugins/search/server/create_api.ts create mode 100644 src/plugins/search/server/es_search/elasticsearch.ts create mode 100644 src/plugins/search/server/es_search/es_search_strategy.ts create mode 100644 src/plugins/search/server/es_search/index.ts create mode 100644 src/plugins/search/server/es_search/plugin.ts create mode 100644 src/plugins/search/server/i_route_handler_search_context.ts create mode 100644 src/plugins/search/server/i_search.ts create mode 100644 src/plugins/search/server/i_search_context.ts create mode 100644 src/plugins/search/server/i_search_setup.ts create mode 100644 src/plugins/search/server/i_search_strategy.ts create mode 100644 src/plugins/search/server/index.ts create mode 100644 src/plugins/search/server/plugin.ts create mode 100644 src/plugins/search/server/routes.ts create mode 100644 src/plugins/search/server/strategy_types.ts create mode 100644 test/plugin_functional/plugins/demo_search/common/index.ts create mode 100644 test/plugin_functional/plugins/demo_search/kibana.json create mode 100644 test/plugin_functional/plugins/demo_search/package.json create mode 100644 test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts create mode 100644 test/plugin_functional/plugins/demo_search/public/index.ts create mode 100644 test/plugin_functional/plugins/demo_search/public/plugin.ts create mode 100644 test/plugin_functional/plugins/demo_search/server/constants.ts create mode 100644 test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts create mode 100644 test/plugin_functional/plugins/demo_search/server/index.ts create mode 100644 test/plugin_functional/plugins/demo_search/server/plugin.ts create mode 100644 test/plugin_functional/plugins/demo_search/tsconfig.json create mode 100644 test/plugin_functional/plugins/search_explorer/kibana.json create mode 100644 test/plugin_functional/plugins/search_explorer/package.json create mode 100644 test/plugin_functional/plugins/search_explorer/public/application.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/demo_strategy.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/do_search.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/documentation.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/es_strategy.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/guide_section.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/index.ts create mode 100644 test/plugin_functional/plugins/search_explorer/public/page.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/plugin.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/public/search_api.tsx create mode 100644 test/plugin_functional/plugins/search_explorer/tsconfig.json diff --git a/src/plugins/search/README.md b/src/plugins/search/README.md new file mode 100644 index 000000000000000..33e6d9ab0bd1aae --- /dev/null +++ b/src/plugins/search/README.md @@ -0,0 +1,13 @@ +# search + +The `search` plugin provides the ability to register search strategies that take in a request +object, and return a response object, of a given shape. + +Both client side search strategies can be registered, as well as server side search strategies. + +The `search` plugin includes two one concrete client side implementations - + `SYNC_SEARCH_STRATEGY` and `ES_SEARCH_STRATEGY` which uses `SYNC_SEARCH_STRATEGY`. There is also one + default server side search strategy, `ES_SEARCH_STRATEGY`. + + Includes the `esSearch` plugin in order to search for data from Elasticsearch using Elasticsearch +DSL. diff --git a/src/plugins/search/common/es_search/elasticsearch.ts b/src/plugins/search/common/es_search/elasticsearch.ts new file mode 100644 index 000000000000000..0d942514c3759c7 --- /dev/null +++ b/src/plugins/search/common/es_search/elasticsearch.ts @@ -0,0 +1,172 @@ +/* + * 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 { SearchResponse, SearchParams } from 'elasticsearch'; + +export interface StringMap { + [key: string]: T; +} + +export type IndexAsString = { + [k: string]: Map[keyof Map]; +} & Map; + +export type Omit = Pick>; + +export interface BoolQuery { + must_not: Array>; + should: Array>; + filter: Array>; +} + +// extending SearchResponse to be able to have typed aggregations + +type AggregationType = + | 'date_histogram' + | 'histogram' + | 'terms' + | 'avg' + | 'top_hits' + | 'max' + | 'min' + | 'percentiles' + | 'sum' + | 'extended_stats' + | 'filter' + | 'filters' + | 'cardinality' + | 'sampler' + | 'value_count'; + +type AggOptions = AggregationOptionMap & { + [key: string]: any; +}; + +// eslint-disable-next-line @typescript-eslint/prefer-interface +export type AggregationOptionMap = { + aggs?: { + [aggregationName: string]: { + [T in AggregationType]?: AggOptions & AggregationOptionMap; + }; + }; +}; + +type SubAggregation = T extends { aggs: any } ? AggregationResultMap : {}; + +// eslint-disable-next-line @typescript-eslint/prefer-interface +type BucketAggregation = { + buckets: Array< + { + key: KeyType; + key_as_string: string; + doc_count: number; + } & (SubAggregation) + >; +}; + +type FilterAggregation = { + doc_count: number; +} & SubAggregation; + +// eslint-disable-next-line @typescript-eslint/prefer-interface +type FiltersAggregation = { + buckets: Array< + { + doc_count: number; + } & SubAggregation + >; +}; + +type SamplerAggregation = SubAggregation & { + doc_count: number; +}; + +interface AggregatedValue { + value: number | null; +} + +type AggregationResultMap = IndexAsString< + { + [AggregationName in keyof AggregationOption]: { + avg: AggregatedValue; + max: AggregatedValue; + min: AggregatedValue; + sum: AggregatedValue; + value_count: AggregatedValue; + // Elasticsearch might return terms with numbers, but this is a more limited type + terms: BucketAggregation; + date_histogram: BucketAggregation; + histogram: BucketAggregation; + top_hits: { + hits: { + total: number; + max_score: number | null; + hits: Array<{ + _source: AggregationOption[AggregationName] extends { + Mapping: any; + } + ? AggregationOption[AggregationName]['Mapping'] + : never; + }>; + }; + }; + percentiles: { + values: { + [key: string]: number; + }; + }; + extended_stats: { + count: number; + min: number | null; + max: number | null; + avg: number | null; + sum: number; + sum_of_squares: number | null; + variance: number | null; + std_deviation: number | null; + std_deviation_bounds: { + upper: number | null; + lower: number | null; + }; + }; + filter: FilterAggregation; + filters: FiltersAggregation; + cardinality: { + value: number; + }; + sampler: SamplerAggregation; + }[AggregationType & keyof AggregationOption[AggregationName]]; + } +>; + +export type IEsRawSearchResponse = Pick< + SearchResponse, + Exclude, 'aggregations'> +> & + (TSearchParams extends { body: Required } + ? { + aggregations?: AggregationResultMap; + } + : {}); + +export interface ESFilter { + [key: string]: { + [key: string]: string | string[] | number | StringMap | ESFilter[]; + }; +} diff --git a/src/plugins/search/common/es_search/index.ts b/src/plugins/search/common/es_search/index.ts new file mode 100644 index 000000000000000..5b605224491b2de --- /dev/null +++ b/src/plugins/search/common/es_search/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export { IEsSearchRequest, IEsSearchResponse, ES_SEARCH_STRATEGY } from './types'; diff --git a/src/plugins/search/common/es_search/types.ts b/src/plugins/search/common/es_search/types.ts new file mode 100644 index 000000000000000..4c90e36d828148b --- /dev/null +++ b/src/plugins/search/common/es_search/types.ts @@ -0,0 +1,31 @@ +/* + * 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 { SearchParams } from 'elasticsearch'; +import { IEsRawSearchResponse } from './elasticsearch'; +import { IKibanaSearchRequest, IKibanaSearchResponse } from '../types'; + +export const ES_SEARCH_STRATEGY = 'es'; + +export interface IEsSearchRequest extends IKibanaSearchRequest { + params: SearchParams; +} + +export interface IEsSearchResponse extends IKibanaSearchResponse { + rawResponse: IEsRawSearchResponse; +} diff --git a/src/plugins/search/common/index.ts b/src/plugins/search/common/index.ts new file mode 100644 index 000000000000000..eb333c67b876dbd --- /dev/null +++ b/src/plugins/search/common/index.ts @@ -0,0 +1,26 @@ +/* + * 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 { ES_SEARCH_STRATEGY } from './es_search'; + +export { IKibanaSearchResponse, IKibanaSearchRequest } from './types'; + +export const DEFAULT_SEARCH_STRATEGY = ES_SEARCH_STRATEGY; + +export { IEsSearchRequest, IEsSearchResponse, ES_SEARCH_STRATEGY } from './es_search'; diff --git a/src/plugins/search/common/types.ts b/src/plugins/search/common/types.ts new file mode 100644 index 000000000000000..2e7a2421d192162 --- /dev/null +++ b/src/plugins/search/common/types.ts @@ -0,0 +1,55 @@ +/* + * 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. + */ + +export interface IKibanaSearchResponse { + /** + * Some responses may contain a unique id to identify the request this response came from. + */ + id?: string; + + /** + * If hasProgressInformation, this should be implemented and return progress + * information. + */ + getPercentComplete?: () => number; + + /** + * If relevant to the search strategy, return a total number + * that represents how progress is indicated. + */ + total?: number; + + /** + * If relevant to the search strategy, return a total number + * that represents how progress is indicated. + */ + loaded?: number; +} + +export interface IKibanaSearchRequest { + /** + * An id can be used to uniquely identify this request. + */ + id?: string; + + /** + * Optionally tell search strategies to output debug information. + */ + debug?: boolean; +} diff --git a/src/plugins/search/kibana.json b/src/plugins/search/kibana.json new file mode 100644 index 000000000000000..5bc1a4f2766af02 --- /dev/null +++ b/src/plugins/search/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "search", + "version": "kibana", + "server": true, + "ui": true +} diff --git a/src/plugins/search/public/constants.ts b/src/plugins/search/public/constants.ts new file mode 100644 index 000000000000000..634d84233ef5c48 --- /dev/null +++ b/src/plugins/search/public/constants.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export const SYNC_SEARCH_STRATEGY = 'SYNC_SEARCH_STRATEGY'; diff --git a/src/plugins/search/public/create_app_mount_context_search.ts b/src/plugins/search/public/create_app_mount_context_search.ts new file mode 100644 index 000000000000000..1b479b8d411b328 --- /dev/null +++ b/src/plugins/search/public/create_app_mount_context_search.ts @@ -0,0 +1,56 @@ +/* + * 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 { ES_SEARCH_STRATEGY } from '../common/es_search'; + +export const createAppMountSearchContext = ( + searchStrategies: TSearchStrategiesMap +): ISearchAppMountContext => { + const getSearchStrategy = ( + strategyName?: K + ): Promise> => { + const strategyProvider = searchStrategies[strategyName ? strategyName : ES_SEARCH_STRATEGY] as + | TSearchStrategyProviderEnhanced + | undefined; + if (!strategyProvider) { + throw new Error(`Strategy with name ${name} does not exist`); + } + return strategyProvider(search); + }; + + const search: ISearchGeneric = (request, options, strategyName) => { + const strategyPromise = getSearchStrategy(strategyName); + return from(strategyPromise).pipe(mergeMap(s => s.search(request, options))); + }; + + return { + search, + getSearchStrategy, + }; +}; diff --git a/src/plugins/search/public/es_search/es_search_strategy.ts b/src/plugins/search/public/es_search/es_search_strategy.ts new file mode 100644 index 000000000000000..afe2c496a102bc5 --- /dev/null +++ b/src/plugins/search/public/es_search/es_search_strategy.ts @@ -0,0 +1,43 @@ +/* + * 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 { Observable } from 'rxjs'; +import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../common'; + +import { + TSearchStrategyProvider, + ISearchStrategy, + SYNC_SEARCH_STRATEGY, + ISearchGeneric, + ISearchContext, +} from '..'; + +export const esClientSearchStrategyProvider: TSearchStrategyProvider = ( + context: ISearchContext, + search: ISearchGeneric +): ISearchStrategy => { + return { + search: (request, options) => + search( + { ...request, serverStrategy: ES_SEARCH_STRATEGY }, + options, + SYNC_SEARCH_STRATEGY + ) as Observable>, + }; +}; diff --git a/src/plugins/search/public/es_search/index.ts b/src/plugins/search/public/es_search/index.ts new file mode 100644 index 000000000000000..38e1627b5adbc99 --- /dev/null +++ b/src/plugins/search/public/es_search/index.ts @@ -0,0 +1,25 @@ +/* + * 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 { EsSearchPlugin } from './plugin'; + +export const plugin: PluginInitializer = ( + initializerContext: PluginInitializerContext +) => new EsSearchPlugin(initializerContext); diff --git a/src/plugins/search/public/es_search/plugin.ts b/src/plugins/search/public/es_search/plugin.ts new file mode 100644 index 000000000000000..217e244e519ae9d --- /dev/null +++ b/src/plugins/search/public/es_search/plugin.ts @@ -0,0 +1,41 @@ +/* + * 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/es_search'; +import { esClientSearchStrategyProvider } from './es_search_strategy'; +import { ISearchSetup } from '../i_search_setup'; + +interface EsSearchSetupDependencies { + search: ISearchSetup; +} + +export class EsSearchPlugin implements Plugin { + constructor(private initializerContext: PluginInitializerContext) {} + public setup(core: CoreSetup, deps: EsSearchSetupDependencies) { + deps.search.registerSearchStrategyProvider( + this.initializerContext.opaqueId, + ES_SEARCH_STRATEGY, + esClientSearchStrategyProvider + ); + } + + public start() {} + public stop() {} +} diff --git a/src/plugins/search/public/i_search.ts b/src/plugins/search/public/i_search.ts new file mode 100644 index 000000000000000..f09dcfcc2d99763 --- /dev/null +++ b/src/plugins/search/public/i_search.ts @@ -0,0 +1,56 @@ +/* + * 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 { Observable } from 'rxjs'; +import { IKibanaSearchRequest } from './types'; +import { IKibanaSearchResponse, DEFAULT_SEARCH_STRATEGY } from '../common'; +import { SYNC_SEARCH_STRATEGY } from './constants'; +import { TStrategyTypes } from './strategy_types'; +import { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from '../common/es_search'; + +export interface ISyncSearchRequest extends IKibanaSearchRequest { + serverStrategy: string; +} + +export interface ISearchOptions { + signal?: AbortSignal; +} + +export interface IRequestTypesMap { + [SYNC_SEARCH_STRATEGY]: ISyncSearchRequest; + [ES_SEARCH_STRATEGY]: IEsSearchRequest; + [key: string]: IKibanaSearchRequest; +} + +export interface IResponseTypesMap { + [SYNC_SEARCH_STRATEGY]: IKibanaSearchResponse; + [ES_SEARCH_STRATEGY]: IEsSearchResponse; + [key: string]: IKibanaSearchResponse; +} + +export type ISearchGeneric = ( + request: IRequestTypesMap[T], + options: ISearchOptions, + strategy?: T +) => Observable; + +export type ISearch = ( + request: IRequestTypesMap[T], + options: ISearchOptions +) => Observable; diff --git a/src/plugins/search/public/i_search_app_mount_context.ts b/src/plugins/search/public/i_search_app_mount_context.ts new file mode 100644 index 000000000000000..93ed06544f12950 --- /dev/null +++ b/src/plugins/search/public/i_search_app_mount_context.ts @@ -0,0 +1,27 @@ +/* + * 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 { ISearchGeneric } from './i_search'; +import { TGetSearchStrategy } from './i_search_strategy'; + +export interface ISearchAppMountContext { + search: ISearchGeneric; + + getSearchStrategy: TGetSearchStrategy; +} diff --git a/src/plugins/search/public/i_search_context.ts b/src/plugins/search/public/i_search_context.ts new file mode 100644 index 000000000000000..6894fdae93d4a3d --- /dev/null +++ b/src/plugins/search/public/i_search_context.ts @@ -0,0 +1,23 @@ +/* + * 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; +} diff --git a/src/plugins/search/public/i_search_setup.ts b/src/plugins/search/public/i_search_setup.ts new file mode 100644 index 000000000000000..3ba718fcc5a56a1 --- /dev/null +++ b/src/plugins/search/public/i_search_setup.ts @@ -0,0 +1,41 @@ +/* + * 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 { ISearchGeneric } from './i_search'; +import { TRegisterSearchStrategyProvider } from './i_search_strategy'; + +/** + * The setup contract exposed by the Search plugin exposes the search strategy extension + * point. + */ +export interface ISearchSetup { + registerSearchStrategyContext: ( + pluginId: symbol, + contextName: TContextName, + provider: IContextProvider + ) => void; + + /** + * Extension point exposed for other plugins to register their own search + * strategies. + */ + registerSearchStrategyProvider: TRegisterSearchStrategyProvider; +} diff --git a/src/plugins/search/public/i_search_strategy.ts b/src/plugins/search/public/i_search_strategy.ts new file mode 100644 index 000000000000000..a8b8744c52bde39 --- /dev/null +++ b/src/plugins/search/public/i_search_strategy.ts @@ -0,0 +1,67 @@ +/* + * 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 { ISearch, ISearchGeneric } from './i_search'; +import { TStrategyTypes } from './strategy_types'; +import { ISearchContext } from './i_search_context'; + +/** + * Search strategy interface contains a search method that takes in + * a request and returns a promise that resolves to a response. + */ +export interface ISearchStrategy { + search: ISearch; +} + +/** + * 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 TSearchStrategyProviderEnhanced = ( + search: ISearchGeneric +) => Promise>; + +/** + * 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 = ( + context: ISearchContext, + search: ISearchGeneric +) => ISearchStrategy; + +/** + * Extension point exposed for other plugins to register their own search + * strategies. + */ +export type TRegisterSearchStrategyProvider = ( + opaqueId: symbol, + name: T, + searchStrategyProvider: TSearchStrategyProvider +) => void; + +export type TSearchStrategiesMap = { + [K in TStrategyTypes]?: TSearchStrategyProviderEnhanced; +}; + +export type TGetSearchStrategy = ( + strategyName: K +) => Promise>; diff --git a/src/plugins/search/public/index.ts b/src/plugins/search/public/index.ts new file mode 100644 index 000000000000000..f8699a1070f371c --- /dev/null +++ b/src/plugins/search/public/index.ts @@ -0,0 +1,47 @@ +/* + * 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 { PluginInitializerContext } from '../../../core/public'; +import { SearchPublicPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new SearchPublicPlugin(initializerContext); +} + +export { ISearchAppMountContext } from './i_search_app_mount_context'; + +export { ISearchSetup } from './i_search_setup'; + +export { SYNC_SEARCH_STRATEGY } from './constants'; + +export { ISearchContext } from './i_search_context'; + +export { IKibanaSearchRequest, IKibanaSearchResponse } from './types'; + +export { + ISearch, + ISearchOptions, + IRequestTypesMap, + IResponseTypesMap, + ISearchGeneric, +} from './i_search'; + +export { TSearchStrategyProvider, ISearchStrategy } from './i_search_strategy'; + +export { IEsSearchResponse, IEsSearchRequest, ES_SEARCH_STRATEGY } from '../common/es_search'; diff --git a/src/plugins/search/public/plugin.ts b/src/plugins/search/public/plugin.ts new file mode 100644 index 000000000000000..f8676215548c9e5 --- /dev/null +++ b/src/plugins/search/public/plugin.ts @@ -0,0 +1,128 @@ +/* + * 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, + CoreStart, + IContextContainer, + PluginOpaqueId, +} 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 { syncSearchStrategyProvider } from './sync_search_strategy'; +import { ISearchContext } from './i_search_context'; +import { SYNC_SEARCH_STRATEGY } from './constants'; +import { ISearchGeneric } from './i_search'; +import { + TSearchStrategyProvider, + ISearchStrategy, + TRegisterSearchStrategyProvider, + TSearchStrategiesMap, +} from './i_search_strategy'; +import { TStrategyTypes } from './strategy_types'; +import { EsSearchPlugin } from './es_search/plugin'; + +/** + * Extends the AppMountContext so other plugins have access + * to search functionality in their applications. + */ +declare module 'kibana/public' { + interface AppMountContext { + search: ISearchAppMountContext; + } +} + +/** + * The search plugin exposes two registration methods for other plugins: + * - registerSearchStrategyProvider for plugins to add their own custom + * search strategies + * - registerSearchStrategyContext for plugins to expose information + * and/or functionality for other search strategies to use + * + * It also comes with two search strategy implementations - SYNC_SEARCH_STRATEGY and ES_SEARCH_STRATEGY. + */ +export class SearchPublicPlugin implements Plugin { + /** + * A mapping of search strategies keyed by a unique identifier. Plugins can use this unique identifier + * to override certain strategy implementations. + */ + private searchStrategies: TSearchStrategiesMap = {}; + + /** + * Exposes context to the search strategies. + */ + private contextContainer?: IContextContainer< + ISearchContext, + ISearchStrategy, + [ISearchGeneric] + >; + + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup): ISearchSetup { + this.contextContainer = core.context.createContextContainer(); + + this.contextContainer!.registerContext(this.initializerContext.opaqueId, 'core', () => core); + + core.application.registerMountContext<'search'>('search', () => { + const { search, getSearchStrategy } = createAppMountSearchContext(this.searchStrategies); + + const appMountApi = { + getSearchStrategy, + search, + }; + + return appMountApi; + }); + + const registerSearchStrategyProvider: TRegisterSearchStrategyProvider = < + T extends TStrategyTypes + >( + plugin: PluginOpaqueId, + name: T, + strategyProvider: TSearchStrategyProvider + ) => { + this.searchStrategies[name] = this.contextContainer!.createHandler(plugin, strategyProvider); + }; + + const api = { + registerSearchStrategyContext: this.contextContainer!.registerContext, + registerSearchStrategyProvider, + }; + + 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. + new EsSearchPlugin(this.initializerContext).setup(core, { search: api }); + + return api; + } + + public start(core: CoreStart) {} + public stop() {} +} diff --git a/src/plugins/search/public/strategy_types.ts b/src/plugins/search/public/strategy_types.ts new file mode 100644 index 000000000000000..3635ec64db5d8ac --- /dev/null +++ b/src/plugins/search/public/strategy_types.ts @@ -0,0 +1,40 @@ +/* + * 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 { ES_SEARCH_STRATEGY } from '../common/es_search'; +import { SYNC_SEARCH_STRATEGY } from './constants'; + +/** + * Contains all known strategy type identifiers that will be used to map to + * request and response shapes. Plugins that wish to add their own custom search + * strategies should extend this type via: + * + * const MY_STRATEGY = 'MY_STRATEGY'; + * + * declare module 'src/plugins/search/public' { + * export interface IRequestTypesMap { + * [MY_STRATEGY]: IMySearchRequest; + * } + * + * export interface IResponseTypesMap { + * [MY_STRATEGY]: IMySearchResponse + * } + * } + */ +export type TStrategyTypes = typeof SYNC_SEARCH_STRATEGY | typeof ES_SEARCH_STRATEGY | string; diff --git a/src/plugins/search/public/sync_search_strategy.ts b/src/plugins/search/public/sync_search_strategy.ts new file mode 100644 index 000000000000000..28234ef04c35438 --- /dev/null +++ b/src/plugins/search/public/sync_search_strategy.ts @@ -0,0 +1,55 @@ +/* + * 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 { from } from 'rxjs'; +import { IKibanaSearchResponse } from '../common'; +import { ISearchContext } from './i_search_context'; +import { ISearch, ISearchOptions, ISyncSearchRequest } from './i_search'; +import { SYNC_SEARCH_STRATEGY } from './constants'; +import { TSearchStrategyProvider, ISearchStrategy } from './i_search_strategy'; + +export const syncSearchStrategyProvider: TSearchStrategyProvider = ( + context: ISearchContext +) => { + if (!context.core) { + throw new Error('core undefined!'); + } + + const search: ISearch = ( + request: ISyncSearchRequest, + options: ISearchOptions + ) => { + const response: Promise = context.core.http.fetch( + `/api/search/${request.serverStrategy}`, + { + method: 'POST', + body: JSON.stringify(request), + signal: options.signal, + } + ); + + return from(response); + }; + + const strategy: ISearchStrategy = { + search, + }; + + return strategy; +}; diff --git a/src/plugins/search/public/types.ts b/src/plugins/search/public/types.ts new file mode 100644 index 000000000000000..c7f2c42cc5b23f7 --- /dev/null +++ b/src/plugins/search/public/types.ts @@ -0,0 +1,25 @@ +/* + * 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 { IKibanaSearchRequest } from '../common'; +export * from '../common/types'; + +export interface ISyncKibanaSearchRequest extends IKibanaSearchRequest { + serverStrategy: string; +} diff --git a/src/plugins/search/server/create_api.ts b/src/plugins/search/server/create_api.ts new file mode 100644 index 000000000000000..feee85058b3d866 --- /dev/null +++ b/src/plugins/search/server/create_api.ts @@ -0,0 +1,48 @@ +/* + * 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 { APICaller } from 'kibana/server'; +import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; +import { ES_SEARCH_STRATEGY } from '../common/es_search'; +import { TSearchStrategiesMap } from './i_search_strategy'; + +export function createApi({ + caller, + searchStrategies, +}: { + searchStrategies: TSearchStrategiesMap; + caller: APICaller; +}) { + const api: IRouteHandlerSearchContext = { + search: async (request, strategyName) => { + const name = strategyName ? strategyName : ES_SEARCH_STRATEGY; + const strategyProvider = searchStrategies[name]; + if (!strategyProvider) { + throw new Error(`No strategy found for ${strategyName}`); + } + // Give providers access to other search stratgies by injecting this function + const strategy = await strategyProvider(caller, api.search); + if (!strategy) { + throw new Error(`No strategy named ${name}`); + } + return strategy.search(request); + }, + }; + return api; +} diff --git a/src/plugins/search/server/es_search/elasticsearch.ts b/src/plugins/search/server/es_search/elasticsearch.ts new file mode 100644 index 000000000000000..6c3aa771328a84a --- /dev/null +++ b/src/plugins/search/server/es_search/elasticsearch.ts @@ -0,0 +1,34 @@ +/* + * 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. + */ + +export interface StringMap { + [key: string]: T; +} + +export type IndexAsString = { + [k: string]: Map[keyof Map]; +} & Map; + +export type Omit = Pick>; + +export interface BoolQuery { + must_not: Array>; + should: Array>; + filter: Array>; +} diff --git a/src/plugins/search/server/es_search/es_search_strategy.ts b/src/plugins/search/server/es_search/es_search_strategy.ts new file mode 100644 index 000000000000000..ea5a1ced3bc7e54 --- /dev/null +++ b/src/plugins/search/server/es_search/es_search_strategy.ts @@ -0,0 +1,53 @@ +/* + * 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 { APICaller } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; +import { ISearchContext } from 'src/plugins/search/server'; +import { IEsSearchRequest, ES_SEARCH_STRATEGY } from '../../common'; +import { ISearchStrategy, TSearchStrategyProvider } from '../i_search_strategy'; + +export const esSearchStrategyProvider: TSearchStrategyProvider = ( + context: ISearchContext, + caller: APICaller +): ISearchStrategy => { + return { + search: async (request: IEsSearchRequest) => { + if (request.debug) { + // eslint-disable-next-line + console.log(JSON.stringify(request, null, 2)); + } + const esSearchResponse = (await caller('search', { + ...request.params, + // TODO: could do something like this here? + // ...getCurrentSearchParams(context), + })) as SearchResponse; + + // The above query will either complete or timeout and throw an error. + // There is no progress indication on this api. + return { + total: esSearchResponse._shards.total, + loaded: + esSearchResponse._shards.failed + + esSearchResponse._shards.skipped + + esSearchResponse._shards.successful, + rawResponse: esSearchResponse, + }; + }, + }; +}; diff --git a/src/plugins/search/server/es_search/index.ts b/src/plugins/search/server/es_search/index.ts new file mode 100644 index 000000000000000..5135be39ed76f97 --- /dev/null +++ b/src/plugins/search/server/es_search/index.ts @@ -0,0 +1,27 @@ +/* + * 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 { PluginInitializerContext } from '../../../../core/server'; +import { EsSearchServerPlugin } from './plugin'; + +export { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from '../../common'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new EsSearchServerPlugin(initializerContext); +} diff --git a/src/plugins/search/server/es_search/plugin.ts b/src/plugins/search/server/es_search/plugin.ts new file mode 100644 index 000000000000000..d8cfcab67612556 --- /dev/null +++ b/src/plugins/search/server/es_search/plugin.ts @@ -0,0 +1,42 @@ +/* + * 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 { ISearchSetup } from '../i_search_setup'; +import { PluginInitializerContext, CoreSetup, Plugin } from '../../../../core/server'; +import { esSearchStrategyProvider } from './es_search_strategy'; +import { ES_SEARCH_STRATEGY } from '../../common'; + +interface IEsSearchDependencies { + search: ISearchSetup; +} + +export class EsSearchServerPlugin implements Plugin { + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, deps: IEsSearchDependencies) { + deps.search.registerSearchStrategyProvider( + this.initializerContext.opaqueId, + ES_SEARCH_STRATEGY, + esSearchStrategyProvider + ); + } + + public start() {} + public stop() {} +} diff --git a/src/plugins/search/server/i_route_handler_search_context.ts b/src/plugins/search/server/i_route_handler_search_context.ts new file mode 100644 index 000000000000000..8a44738a1dcfadd --- /dev/null +++ b/src/plugins/search/server/i_route_handler_search_context.ts @@ -0,0 +1,24 @@ +/* + * 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 { ISearchGeneric } from './i_search'; + +export interface IRouteHandlerSearchContext { + search: ISearchGeneric; +} diff --git a/src/plugins/search/server/i_search.ts b/src/plugins/search/server/i_search.ts new file mode 100644 index 000000000000000..358c3dad3f3cc5d --- /dev/null +++ b/src/plugins/search/server/i_search.ts @@ -0,0 +1,42 @@ +/* + * 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 { IKibanaSearchResponse, IKibanaSearchRequest } from '../common'; +import { TStrategyTypes } from './strategy_types'; +import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../common/es_search'; +import { IEsSearchRequest } from './es_search'; + +export interface IRequestTypesMap { + [ES_SEARCH_STRATEGY]: IEsSearchRequest; + [key: string]: IKibanaSearchRequest; +} + +export interface IResponseTypesMap { + [ES_SEARCH_STRATEGY]: IEsSearchResponse; + [key: string]: IKibanaSearchResponse; +} + +export type ISearchGeneric = ( + request: IRequestTypesMap[T], + strategy?: T +) => Promise; + +export type ISearch = ( + request: IRequestTypesMap[T] +) => Promise; diff --git a/src/plugins/search/server/i_search_context.ts b/src/plugins/search/server/i_search_context.ts new file mode 100644 index 000000000000000..874c797a9f54175 --- /dev/null +++ b/src/plugins/search/server/i_search_context.ts @@ -0,0 +1,23 @@ +/* + * 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/server'; + +export interface ISearchContext { + core: CoreSetup; +} diff --git a/src/plugins/search/server/i_search_setup.ts b/src/plugins/search/server/i_search_setup.ts new file mode 100644 index 000000000000000..0477dc332ea2caa --- /dev/null +++ b/src/plugins/search/server/i_search_setup.ts @@ -0,0 +1,51 @@ +/* + * 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, APICaller } from 'kibana/server'; +import { ISearchContext } from './i_search_context'; +import { IResponseTypesMap, IRequestTypesMap } from './i_search'; +import { TRegisterSearchStrategyProvider } from './i_search_strategy'; +import { TStrategyTypes } from './strategy_types'; +import { DEFAULT_SEARCH_STRATEGY } from '../common'; + +/** + * The setup contract exposed by the Search plugin exposes the search strategy extension + * point. + */ +export interface ISearchSetup { + registerSearchStrategyContext: ( + pluginId: symbol, + strategyName: TContextName, + provider: IContextProvider + ) => void; + + /** + * Extension point exposed for other plugins to register their own search + * strategies. + */ + registerSearchStrategyProvider: TRegisterSearchStrategyProvider; + + __LEGACY: { + search: ( + caller: APICaller, + request: IRequestTypesMap[T], + strategyName?: T + ) => Promise; + }; +} diff --git a/src/plugins/search/server/i_search_strategy.ts b/src/plugins/search/server/i_search_strategy.ts new file mode 100644 index 000000000000000..e7e747636cdb2f1 --- /dev/null +++ b/src/plugins/search/server/i_search_strategy.ts @@ -0,0 +1,70 @@ +/* + * 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 { APICaller } from 'kibana/server'; +import { ISearch, ISearchGeneric } from './i_search'; +import { TStrategyTypes } from './strategy_types'; +import { ISearchContext } from './i_search_context'; + +/** + * Search strategy interface contains a search method that takes in + * a request and returns a promise that resolves to a response. + */ +export interface ISearchStrategy { + search: ISearch; +} + +/** + * 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 TSearchStrategyProviderEnhanced = ( + caller: APICaller, + search: ISearchGeneric +) => Promise>; + +/** + * 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 = ( + context: ISearchContext, + caller: APICaller, + search: ISearchGeneric +) => ISearchStrategy; + +/** + * Extension point exposed for other plugins to register their own search + * strategies. + */ +export type TRegisterSearchStrategyProvider = ( + opaqueId: symbol, + name: T, + searchStrategyProvider: TSearchStrategyProvider +) => void; + +export type TSearchStrategiesMap = { + [K in TStrategyTypes]?: TSearchStrategyProviderEnhanced; +}; + +export type TGettSearchStrategy = ( + strategyName: K +) => Promise>; diff --git a/src/plugins/search/server/index.ts b/src/plugins/search/server/index.ts new file mode 100644 index 000000000000000..4004e1c98827ca4 --- /dev/null +++ b/src/plugins/search/server/index.ts @@ -0,0 +1,34 @@ +/* + * 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 { PluginInitializerContext } from '../../../core/server'; +import { SearchServerPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new SearchServerPlugin(initializerContext); +} + +export { ISearchSetup } from './i_search_setup'; +export * from '../common'; + +export { ISearchContext } from './i_search_context'; + +export { IRequestTypesMap, IResponseTypesMap } from './i_search'; + +export { TStrategyTypes } from './strategy_types'; diff --git a/src/plugins/search/server/plugin.ts b/src/plugins/search/server/plugin.ts new file mode 100644 index 000000000000000..a212f395a7f47b3 --- /dev/null +++ b/src/plugins/search/server/plugin.ts @@ -0,0 +1,105 @@ +/* + * 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 { + PluginInitializerContext, + Plugin, + CoreSetup, + IContextContainer, + APICaller, +} from '../../../core/server'; +import { registerSearchRoute } from './routes'; +import { ISearchSetup } from './i_search_setup'; +import { ISearchContext } from './i_search_context'; +import { createApi } from './create_api'; +import { ISearchGeneric } from './i_search'; +import { + TSearchStrategiesMap, + ISearchStrategy, + TRegisterSearchStrategyProvider, +} from './i_search_strategy'; +import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; +import { EsSearchServerPlugin } from './es_search/plugin'; + +declare module 'kibana/server' { + interface RequestHandlerContext { + search?: IRouteHandlerSearchContext; + } +} + +export class SearchServerPlugin implements Plugin { + private searchStrategies: TSearchStrategiesMap = {}; + + private contextContainer?: IContextContainer< + ISearchContext, + ISearchStrategy, + [APICaller, ISearchGeneric] + >; + + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup): ISearchSetup { + const router = core.http.createRouter(); + registerSearchRoute(router); + + this.contextContainer = core.context.createContextContainer(); + + core.http.registerRouteHandlerContext<'search'>('search', context => { + const searchAPI = createApi({ + caller: context.core!.elasticsearch.dataClient.callAsCurrentUser, + searchStrategies: this.searchStrategies, + }); + return searchAPI; + }); + + this.contextContainer!.registerContext(this.initializerContext.opaqueId, 'core', () => core); + + const registerSearchStrategyProvider: TRegisterSearchStrategyProvider = ( + plugin, + name, + strategyProvider + ) => { + this.searchStrategies[name] = this.contextContainer!.createHandler(plugin, strategyProvider); + }; + + const api: ISearchSetup = { + registerSearchStrategyContext: this.contextContainer!.registerContext, + registerSearchStrategyProvider, + __LEGACY: { + search: (caller, request, strategyName) => { + const searchAPI = createApi({ + caller, + searchStrategies: this.searchStrategies, + }); + return searchAPI.search(request, strategyName); + }, + }, + }; + + // 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. + new EsSearchServerPlugin(this.initializerContext).setup(core, { search: api }); + + return api; + } + + public start() {} + public stop() {} +} diff --git a/src/plugins/search/server/routes.ts b/src/plugins/search/server/routes.ts new file mode 100644 index 000000000000000..a384fd31c723fe8 --- /dev/null +++ b/src/plugins/search/server/routes.ts @@ -0,0 +1,48 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +// Not working in NP +// import 'abortcontroller-polyfill'; +import { IRouter } from '../../../core/server'; + +export function registerSearchRoute(router: IRouter): void { + router.post( + { + path: '/api/search/{strategy}', + validate: { + params: schema.object({ strategy: schema.string() }), + + query: schema.object({}, { allowUnknowns: true }), + + body: schema.object({}, { allowUnknowns: true }), + }, + }, + async (context, request, res) => { + const searchRequest = request.body; + const strategy = request.params.strategy; + try { + const response = await context.search!.search(searchRequest, strategy); + return res.ok({ body: response }); + } catch (err) { + return res.internalError({ body: err }); + } + } + ); +} diff --git a/src/plugins/search/server/strategy_types.ts b/src/plugins/search/server/strategy_types.ts new file mode 100644 index 000000000000000..29e43fa0390e180 --- /dev/null +++ b/src/plugins/search/server/strategy_types.ts @@ -0,0 +1,39 @@ +/* + * 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 { ES_SEARCH_STRATEGY } from '../common/es_search'; + +/** + * Contains all known strategy type identifiers that will be used to map to + * request and response shapes. Plugins that wish to add their own custom search + * strategies should extend this type via: + * + * const MY_STRATEGY = 'MY_STRATEGY'; + * + * declare module 'src/plugins/search/server' { + * export interface IRequestTypesMap { + * [MY_STRATEGY]: IMySearchRequest; + * } + * + * export interface IResponseTypesMap { + * [MY_STRATEGY]: IMySearchResponse + * } + * } + */ +export type TStrategyTypes = typeof ES_SEARCH_STRATEGY | string; diff --git a/test/plugin_functional/plugins/demo_search/common/index.ts b/test/plugin_functional/plugins/demo_search/common/index.ts new file mode 100644 index 000000000000000..00c593c2a28a3f3 --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/common/index.ts @@ -0,0 +1,34 @@ +/* + * 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 { + IKibanaSearchRequest, + IKibanaSearchResponse, +} from '../../../../../src/plugins/search/common'; + +export const DEMO_SEARCH_STRATEGY = 'DEMO_SEARCH_STRATEGY'; + +export interface IDemoRequest extends IKibanaSearchRequest { + mood: string | 'sad' | 'happy'; + name: string; +} + +export interface IDemoResponse extends IKibanaSearchResponse { + greeting: string; +} diff --git a/test/plugin_functional/plugins/demo_search/kibana.json b/test/plugin_functional/plugins/demo_search/kibana.json new file mode 100644 index 000000000000000..cc29b9849980b9e --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "demoSearch", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["demo_search"], + "server": true, + "ui": true, + "requiredPlugins": ["search"], + "optionalPlugins": [] +} diff --git a/test/plugin_functional/plugins/demo_search/package.json b/test/plugin_functional/plugins/demo_search/package.json new file mode 100644 index 000000000000000..1f4fa1421906ad4 --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/package.json @@ -0,0 +1,17 @@ +{ + "name": "demo_data_search", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/demo_data_search", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.5.3" + } +} diff --git a/test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts b/test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts new file mode 100644 index 000000000000000..792ffd3a1a38186 --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts @@ -0,0 +1,70 @@ +/* + * 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 { Observable } from 'rxjs'; +import { + ISearchContext, + SYNC_SEARCH_STRATEGY, + ISearchGeneric, +} from '../../../../../src/plugins/search/public'; +import { TSearchStrategyProvider, ISearchStrategy } from '../../../../../src/plugins/search/public'; + +import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; + +/** + * This demo search strategy provider simply provides a shortcut for calling the DEMO_SEARCH_STRATEGY + * on the server side, without users having to pass it in explicitly, and it takes advantage of the + * already registered SYNC_SEARCH_STRATEGY that exists on the client. + * + * so instead of callers having to do: + * + * ``` + * context.search( + * { ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, + * options, + * SYNC_SEARCH_STRATEGY + * ) as Observable, + *``` + + * They can instead just do + * + * ``` + * context.search(request, options, DEMO_SEARCH_STRATEGY); + * ``` + * + * and are ensured type safety in regard to the request and response objects. + * + * @param context - context supplied by other plugins. + * @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 +): ISearchStrategy => { + return { + search: (request, options) => + search( + { ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, + options, + SYNC_SEARCH_STRATEGY + ) as Observable, + }; +}; diff --git a/test/plugin_functional/plugins/demo_search/public/index.ts b/test/plugin_functional/plugins/demo_search/public/index.ts new file mode 100644 index 000000000000000..7790c2950ac22c2 --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/public/index.ts @@ -0,0 +1,28 @@ +/* + * 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 { DemoDataPlugin } from './plugin'; + +export { DEMO_SEARCH_STRATEGY } from '../common'; + +export const plugin: PluginInitializer = ( + initializerContext: PluginInitializerContext +) => new DemoDataPlugin(initializerContext); diff --git a/test/plugin_functional/plugins/demo_search/public/plugin.ts b/test/plugin_functional/plugins/demo_search/public/plugin.ts new file mode 100644 index 000000000000000..c542202820f354a --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/public/plugin.ts @@ -0,0 +1,52 @@ +/* + * 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 { ISearchSetup } from '../../../../../src/plugins/search/public'; +import { Plugin, CoreSetup, PluginInitializerContext } from '../../../../../src/core/public'; +import { DEMO_SEARCH_STRATEGY } from '../common'; +import { demoClientSearchStrategyProvider } from './demo_search_strategy'; +import { IDemoRequest, IDemoResponse } from '../common'; + +interface DemoDataSearchSetupDependencies { + search: ISearchSetup; +} + +declare module '../../../../../src/plugins/search/public' { + export interface IRequestTypesMap { + [DEMO_SEARCH_STRATEGY]: IDemoRequest; + } + + export interface IResponseTypesMap { + [DEMO_SEARCH_STRATEGY]: IDemoResponse; + } +} + +export class DemoDataPlugin implements Plugin { + constructor(private initializerContext: PluginInitializerContext) {} + public setup(core: CoreSetup, deps: DemoDataSearchSetupDependencies) { + deps.search.registerSearchStrategyProvider( + this.initializerContext.opaqueId, + DEMO_SEARCH_STRATEGY, + demoClientSearchStrategyProvider + ); + } + + public start() {} + public stop() {} +} diff --git a/test/plugin_functional/plugins/demo_search/server/constants.ts b/test/plugin_functional/plugins/demo_search/server/constants.ts new file mode 100644 index 000000000000000..11c258a21d5a860 --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/server/constants.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export const FAKE_PROGRESS_STRATEGY = 'FAKE_PROGRESS_STRATEGY'; diff --git a/test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts b/test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts new file mode 100644 index 000000000000000..86e16b33d172cd0 --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts @@ -0,0 +1,36 @@ +/* + * 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 { TSearchStrategyProvider } from 'src/plugins/search/server/i_search_strategy'; +import { DEMO_SEARCH_STRATEGY } from '../common'; + +export const demoSearchStrategyProvider: TSearchStrategyProvider< + typeof DEMO_SEARCH_STRATEGY +> = () => { + return { + search: request => { + return Promise.resolve({ + greeting: + request.mood === 'happy' + ? `Lovely to meet you, ${request.name}` + : `Hope you feel better, ${request.name}`, + }); + }, + }; +}; diff --git a/test/plugin_functional/plugins/demo_search/server/index.ts b/test/plugin_functional/plugins/demo_search/server/index.ts new file mode 100644 index 000000000000000..6289b684b2b1e70 --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/server/index.ts @@ -0,0 +1,25 @@ +/* + * 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 { PluginInitializerContext, PluginInitializer } from 'kibana/server'; +import { DemoDataPlugin } from './plugin'; + +export const plugin: PluginInitializer = ( + initializerContext: PluginInitializerContext +) => new DemoDataPlugin(initializerContext); diff --git a/test/plugin_functional/plugins/demo_search/server/plugin.ts b/test/plugin_functional/plugins/demo_search/server/plugin.ts new file mode 100644 index 000000000000000..9eec239e5dbc4dd --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/server/plugin.ts @@ -0,0 +1,52 @@ +/* + * 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 'kibana/server'; +import { ISearchSetup } from '../../../../../src/plugins/search/server'; +import { demoSearchStrategyProvider } from './demo_search_strategy'; +import { DEMO_SEARCH_STRATEGY, IDemoRequest, IDemoResponse } from '../common'; + +interface IDemoSearchExplorerDeps { + search: ISearchSetup; +} + +declare module '../../../../../src/plugins/search/server' { + export interface IRequestTypesMap { + [DEMO_SEARCH_STRATEGY]: IDemoRequest; + } + + export interface IResponseTypesMap { + [DEMO_SEARCH_STRATEGY]: IDemoResponse; + } +} + +export class DemoDataPlugin implements Plugin { + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, deps: IDemoSearchExplorerDeps) { + deps.search.registerSearchStrategyProvider( + this.initializerContext.opaqueId, + DEMO_SEARCH_STRATEGY, + demoSearchStrategyProvider + ); + } + + public start() {} + public stop() {} +} diff --git a/test/plugin_functional/plugins/demo_search/tsconfig.json b/test/plugin_functional/plugins/demo_search/tsconfig.json new file mode 100644 index 000000000000000..b311e943160135e --- /dev/null +++ b/test/plugin_functional/plugins/demo_search/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../../typings/**/*" + ], + "exclude": [] +} diff --git a/test/plugin_functional/plugins/search_explorer/kibana.json b/test/plugin_functional/plugins/search_explorer/kibana.json new file mode 100644 index 000000000000000..dc3b4dd18f59d51 --- /dev/null +++ b/test/plugin_functional/plugins/search_explorer/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "search_explorer", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["search_explorer"], + "server": false, + "ui": true, + "requiredPlugins": ["search", "demoSearch"], + "optionalPlugins": [] +} diff --git a/test/plugin_functional/plugins/search_explorer/package.json b/test/plugin_functional/plugins/search_explorer/package.json new file mode 100644 index 000000000000000..9a5e0e83a2207f1 --- /dev/null +++ b/test/plugin_functional/plugins/search_explorer/package.json @@ -0,0 +1,17 @@ +{ + "name": "search_explorer", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/search_explorer", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.5.3" + } +} diff --git a/test/plugin_functional/plugins/search_explorer/public/application.tsx b/test/plugin_functional/plugins/search_explorer/public/application.tsx new file mode 100644 index 000000000000000..6fea5348b9b3524 --- /dev/null +++ b/test/plugin_functional/plugins/search_explorer/public/application.tsx @@ -0,0 +1,122 @@ +/* + * 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 React from 'react'; +import ReactDOM from 'react-dom'; +import { BrowserRouter as Router, Route, withRouter, RouteComponentProps } from 'react-router-dom'; + +import { + EuiPage, + EuiPageSideBar, + // @ts-ignore + EuiSideNav, +} from '@elastic/eui'; + +import { AppMountContext, AppMountParameters } 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'; + +const Home = () => ; + +interface PageDef { + title: string; + id: string; + component: React.ReactNode; +} + +type NavProps = RouteComponentProps & { + navigateToApp: AppMountContext['core']['application']['navigateToApp']; + pages: PageDef[]; +}; + +const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => { + const navItems = pages.map(page => ({ + id: page.id, + name: page.title, + onClick: () => history.push(`/${page.id}`), + 'data-test-subj': page.id, + })); + + return ( + + ); +}); + +const buildPage = (page: PageDef) => {page.component}; + +const SearchApp = ({ basename, context }: { basename: string; context: AppMountContext }) => { + const pages: PageDef[] = [ + { + id: 'home', + title: 'Home', + component: , + }, + { + title: 'Search API', + id: 'searchAPI', + component: , + }, + { + title: 'ES search strategy', + id: 'defaultSearch', + component: , + }, + { + title: 'Demo search strategy', + id: 'fakeSearch', + component: , + }, + ]; + + const routes = pages.map((page, i) => ( + buildPage(page)} /> + )); + + return ( + + + +